Latex3: Declaración de variables locales

Creado en 18 oct. 2017  ·  49Comentarios  ·  Fuente: latex3/latex3

El documento El paquete expl3 y la programación LaTeX3 (v. 2017/09/18) establece en la p. 7:

La convención de codificación de LaTeX3 es que todas las variables deben declararse antes de su uso.

De manera similar, el documento The LaTeX3 Interfaces (v. 2017/09/18) establece en la p. 10:

... a diferencia de las variables, que siempre deben declararse

expl3 no proporciona facilidades para declarar una variable localmente; solo a nivel mundial. Sin embargo, todavía es posible crear una variable local implícitamente usando las diversas funciones \..._set:N... .

Es un principio de la programación estructurada que debe evitarse el vertido innecesario de variables en el ámbito global. El lenguaje TeX, como todos los lenguajes de programación de alto nivel que conozco (incluidos C, C #, Fortran, Java, Lisp, Pascal y Python), proporciona una construcción de alcance para crear variables locales, es decir, grupos. Además, el propio lenguaje de programación LaTeX3, afortunadamente, admite la creación de variables locales, a pesar de lo que sugieren los manuales.

En mi opinión, las advertencias citadas anteriormente deben eliminarse de los manuales, y debe explicarse que las variables pueden y deben crearse localmente siempre que sea posible.

Además, si de hecho se desea fomentar un estilo de programación en el que todas las variables se declaren antes de que se utilicen (personalmente creo que esto es una cuestión de estilo de programación que debería dejarse al gusto individual del programador), las funciones deben ser proporcionado para declarar variables locales al igual que hay funciones para declarar variables globales.

expl3 feature-request

Todos 49 comentarios

La posición actual sigue una serie de experimentos del equipo para establecer un patrón que funcione con los fundamentos de TeX que admiten expl3 . En particular, es importante tener en cuenta que las variables pueden implementarse usando macros o usando registros, y tener en cuenta cómo funciona la agrupación TeX.

Cuando usamos registros (_e.g._ para el tipo int ) necesitamos un asignador para vincular el número de registro y cs que estamos usando para el acceso. Por el contrario, para el almacenamiento basado en macros que no es necesario. Por lo tanto, aunque \cs_set_eq:NN se puede usar para generar nuevos tl nombres (por ejemplo), fallará con cualquier cosa que use registros:

\int_set_eq:NN \l_undeclared_int \l_tmpa_in

generará un error.

Es posible crear un asignador de registros local, y lo hemos probado en el pasado para _e.g._ \int_local_new:N . (Consulte el paquete etex para una implementación de este tipo, o revise el historial de expl3 .) El equipo finalmente decidió no hacerlo ya que no parecía "encajar" bien con el alcance de TeX. La clave aquí es que la agrupación TeX no se basa en declaraciones sino en grupos explícitos, y que existe una variable local dentro de cualquier grupo anidado:

\begingroup
  \def\foo{}
  \begingroup
  \show\foo

Cuando probamos la _creación_ local de variables, parecía que existía el peligro de oscurecer cómo funcionaba la agrupación TeX, y que los programadores podrían ser engañados.

Por lo tanto, decidimos ir con la _declaración_ 'global' de las variables, pero con la _configuración_ local y global de tales variables (la convención \l_... _versus \g_... ). Esto localiza lo importante: el valor. Por lo tanto, recomendamos que todas las declaraciones estén en el nivel superior.

\tl_new:N \l_my_tl
\int_new:N \l_my_int

...
\cs_new_protected:Npn \my_func:nn #1#2
  {
    \group_begin:
      \tl_set:Nx \l_my_tl { \tl_lower_case:n {#1} }

Hasta la fecha, este patrón parece funcionar bien para las tareas para las que se ha utilizado expl3 .

En tex, incluso las variables definidas localmente no se destruyen completamente después del final del grupo. Todavía usan algo de espacio de cadena: https://tex.stackexchange.com/questions/316999/release-space-in-the-string-pool. Entonces, en mi humilde opinión, es mejor ver las variables como objetos globales.

@ u-fischer Buen punto: no uno que consideráramos en ese momento, pero que vale la pena tenerlo en cuenta.

En mi opinión, este enfoque es erróneo. Va en contra de los principios de la programación estructurada y va en contra de TeX, que permite la creación de variables locales. También hace que LaTeX3 sea inconsistente, ya que algunos tipos de datos se adaptan a variables locales y otros no.

Me gustaría solicitarle que restablezca la creación local de variables de todo tipo. Esto les da a los programadores una opción: aquellos que prefieren definir todas las variables globalmente pueden hacerlo, y aquellos que desean definir algunas globalmente y otras localmente también pueden hacerlo.

Si hay advertencias, en cuanto a memoria o de otro tipo, se pueden mencionar en la documentación.

El 18 de octubre de 2017 a las 21:17, EvanAad [email protected] escribió:

En mi opinión, este es un enfoque equivocado y me gustaría solicitar que
se restablece la creación local de variables. Esto le da a los programadores una
elección: aquellos que prefieren definir todas las variables globalmente pueden hacerlo, y
aquellos que deseen definir algunos globalmente y algunos localmente también pueden hacerlo.

para las variables basadas en un tipo de registro, está asignando recursos de un
grupo global fijo por lo que la asignación global del nombre es de hecho mucho más
natural. Dado que asumimos etex, hay más de 256 registros de
cada tipo, el comportamiento de asignación tal vez se pueda ocultar más de lo que
podría con el texto clásico, pero sigue siendo una característica fundamental de la
sistema TeX subyacente. expl3 nunca puede ser una programación completamente general
idioma: tiene que funcionar con el sistema TeX subyacente. Tú no debes ser
en comparación con C # pero con otros lenguajes basados ​​en TeX y en particular con
\ newcount y amigos.

Tampoco es realmente cierto decir que todos los demás lenguajes permiten que las variables sean
declaradas en ámbitos locales, fortran, por ejemplo, solo permite que las variables sean
declarado al inicio de una función / subrutina.

Nada de lo anterior significa que definitivamente no agregaríamos una declaración local.
sistema, pero apelar a lenguajes de propósito general no es un buen caso de uso.
Debería haber casos de uso razonables dentro de la composición tipográfica de texto donde
utilizar un sistema de declaración global era un problema.

El hecho de que Fortran requiera que las variables se declaren al comienzo de una función / subrutina no significa que esas variables sean globales. Si insiste en fomentar un estilo de programación donde se deben declarar todas las variables, que así sea, pero luego proporcione funciones para declarar variables locales correspondientes a las que declaran globales.

La razón fundamental para la determinación del alcance y las variables locales es ante todo conceptual. El hecho de que el motor TeX subyacente no libera algunos de los recursos asociados con las variables locales no debe descartarse, pero, en mi opinión, esta no puede ser la razón para borrar la noción de alcance y variables locales. A excepción de los programadores de energía, la implementación subyacente no es motivo de preocupación; y para programadores avanzados, se pueden ofrecer consejos y advertencias en la documentación.

apelar a lenguajes de propósito general no es un buen caso de uso.

Yo diría que divergir de los principios de programación estructurada que se han implementado en prácticamente todos los lenguajes de programación desde la década de 1960 hasta hoy, incluido TeX, debería ser algo para lo que el equipo de LaTeX3 debería tener razones muy convincentes para hacerlo. La carga de la prueba debería recaer en aquellos que deseen abolir lo que se considera ubicuamente como una buena práctica de programación y lo que ya existe en TeX; no a quienes deseen conservarlo.

@EvanAad La lista de globalmente para que la asignación no toque un registro ya asignado.

Digamos que a su variable entera local \x se le asigna el registro 100 que ya está tomado por la variable \y en un nivel superior; usar \y al mismo nivel donde \x se define localmente sería simplemente desastroso. Por lo tanto, se necesita la contabilidad global de los registros asignados, lo que dificulta mucho la liberación de los registros asignados localmente. Reservar un bloque de registros para asignaciones locales no es una solución.

No digo que no se pueda hacer, pero no creo que valga la pena el dolor.

se está perdiendo el hecho de que \ newcount no es simplemente local (o globalmente)
declarar un nombre, es asociar un nombre con un fijo definido externamente
recurso. sólo hay un registro de recuento 42 y todos los nombres que
se declaran como un recuento medio 42 se refieren al mismo registro, ya sea que
la asignación de nombre es local o global. Como mencioné, comparaciones con otros
los idiomas no son tan útiles, pero si desea una comparación, debe
comparar la asignación de flujos de archivos o algo así: no siempre se puede aislar
declaraciones locales cuando se interactúan con una definición externa
recurso.

El 18 de octubre de 2017 a las 21:43, EvanAad [email protected] escribió:

El hecho de que Fortran requiera que las variables se declaren al comienzo de una
función / subrutina no significa que esas variables sean globales. Como yo
escribió, si insiste en fomentar un estilo de programación donde todas las variables
debe ser declarado, que así sea, pero luego proporcionar funciones para declarar local
variables correspondientes a las de declarar globales.

La razón fundamental para la determinación del alcance y las variables locales es primordial
conceptual. El hecho de que el motor TeX subyacente no libera algunos
de los recursos asociados con las variables locales no debe descartarse,
pero, en mi opinión, esta no puede ser la razón para borrar la noción
de alcance y variables locales. A excepción de los programadores de energía, el
la implementación subyacente no es motivo de preocupación; y para programadores de energía, consejos
y se pueden ofrecer salvedades en la documentación.

-
Estás recibiendo esto porque hiciste un comentario.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/latex3/latex3/issues/410#issuecomment-337721923 , o silenciar
la amenaza
https://github.com/notifications/unsubscribe-auth/ABNcAimMfBDqA-e96Q7tkS-ERr5fv_2Mks5stmLqgaJpZM4P-Mpq
.

El punto crucial es que, como mencionaron Joseph y David, permitir que int s se definan localmente se puede (y se ha hecho). Creo que vale la pena con el fin de crear un lenguaje que presenta una capa de abstracción consistente que está de acuerdo con los principios de la programación estructurada.

Sin embargo, decir que vale la pena es fácil para mí decirlo, porque no soy yo quien sufre el dolor de implementar el lenguaje LaTeX3. Así que adoptemos un enfoque misericordioso y digamos que, de hecho, no vale la pena el dolor. Multa. En este caso, divida los tipos de datos en dos categorías: los que permiten la creación de variables locales y los que no. Y describa estas categorías en los manuales. No diga: todas las variables deben declararse antes de su uso. Diga: "Hay dos categorías de tipos de datos. La primera, que consta de cs , tl , clist , ... acomoda las variables locales, y la segunda, que consta de int , ... no lo hace ". Y explique por qué existe la segunda categoría. De esta manera, la documentación es fiel a los hechos, y los programadores están bien informados y tienen opciones. Algunos programas no necesitan utilizar tipos de datos de la segunda categoría en absoluto, y en estos casos, ¿por qué debería el programador declarar variables globales?

@EvanAad Sospecho que esto se debe a que estoy acostumbrado a la programación TeX (incluidas las restricciones 'tradicionales' de TeX90), pero no estoy seguro en este momento de cuál es el problema con el enfoque actual. Apoyamos y de hecho fomentamos las variables asignadas localmente: son muy comunes y de hecho forman parte de la sintaxis que tenemos para nombrar variables ( \l_... / \g_... ). El hecho de que estén asignados / 'reservados' globalmente no interfiere con eso.

Por cierto, no queremos vincular las interfaces a la implementación: por ejemplo, el tipo de datos prop ha tenido al menos un par de implementaciones diferentes que conozco, una de las cuales usa registros y la actual usa macros.

Para agregar una nota más a esto, la gota que colmó el vaso (en mi memoria) de que el registro local / sistema de asignación de variables se cayó fue debido a una inconsistencia . Debido a que las macros y los registros se comportaron de manera diferente, terminó con un comportamiento diferente al escribir

\group_begin:
  \int_new_local:N \l_tmpa_int
  \int_gset:Nn \l_tmpa_int {7}
\group_end:
% `\l_tmpa_int` undefined

vs

\group_begin:
  \tl_new_local:N \l_tmpa_tl
  \tl_gset:Nn \l_tmpa_tl {7}
\group_end:
% `\l_tmpa_tl` defined as `7`

Y la única forma de resolver este problema sería escribir un sistema de asignación de macros, que nunca se consideró seriamente debido a los gastos generales. (TeX se ralentiza cuanto más variables se definen, por lo que duplicar el número de listas de tokens podría haber afectado el rendimiento de forma notable).

Sigo pensando que expl3 hizo lo correcto al abstraer macros y registros en funciones que se ven y se sienten iguales, y si la desventaja es que la sintaxis no admite naturalmente variables y registros verdaderamente locales, que han visto uso extremadamente limitado en TeX debido a las razones descritas anteriormente, entonces, desde mi perspectiva, esa es una compensación aceptable.

@wspr Pero los ejemplos que citó como los que rompieron la espalda del camello son ejemplos en los que el programador abusó del lenguaje asignando globalmente a una variable local. Esto está en el programador. El sistema actual no resuelve el problema del abuso del lenguaje, ya que un programador aún puede usar una variable \l_... como si fuera global.

... que está de acuerdo con los principios de la programación estructurada.

Esa es una declaración bastante audaz dado que hay más de un conjunto de
principios y dado que los intentos de unir diferentes principios en
Los superconjuntos han creado lenguajes grandes pero inútiles en el pasado.

El punto crucial no es necesariamente que se pueda hacer algo (como
dentro de las bases completas de Turing todas las cosas son iguales en ese nivel) pero
si se puede hacer algo de manera eficiente y coherente, etc.

@wspr : ¿Por qué cerró este tema como un matón? Es cualquier cosa menos resuelto.

@EvanAad : solo trato de mantener las cosas ordenadas. La discusión ciertamente puede continuar y si se justifica volveremos a abrir.

Estoy bloqueando estos problemas temporalmente debido a algunos comentarios acalorados.

Creo que esta es una pregunta relevante. Un resultado de esta discusión debería ser al menos que describamos más cuidadosamente en el documento por qué elegimos solo declaraciones globales. @EvanAad No creo que \int_local_new:N hipotético o un sistema diferente de su elección con la semántica de su elección? Ayudaría a enmarcar la discusión en un escenario más concreto, y podemos analizar los beneficios / inconvenientes.

Presumiblemente tenemos que esperar a que se levante la cerradura, no sé cómo hacerlo. En cualquier caso, la mayoría de las personas en esta conversación dormirán durante algunas horas. (Por cierto, no creo que copiar el # 410 en el # 411 como una discusión confusa sea muy razonable).

Pasando por alto el hecho de que esta es una discusión bastante detallada, creo que vale la pena resumir la historia técnica y social que nos llevó a donde estamos.

A nivel técnico, TeX proporciona registros y macros para el almacenamiento. Las macros pueden simplemente 'crearse' usando \def , pero para usar registros por nombre, necesitamos algún asignador para vincular el número de registro global, _e.g._ \count40 , con algún nombre, por ejemplo, \mycount . Eso se ha proporcionado desde el 'día uno', con el modelo simple TeX \newcount , _etc._ proporcionando el modelo. En pocas palabras, \newcount asigna globalmente y, a pesar del nombre, no realiza ninguna verificación. Otros formatos, principalmente LaTeX2e y ConTeXt, han adoptado este enfoque en términos generales. En general, también han adoptado la idea de que los registros individuales se asignan a continuación, ya sea local o globalmente, ya que esto evita la acumulación de la pila de guardado.

TeX90 proporcionó solo 256 registros de los tipos comunes, por lo que la reutilización de registros en contextos locales fue vital. Uno ve que en _e.g._ graphics donde para mantener el código 'sano', un gran número de registros reciben nuevos nombres dentro de un grupo, y se usan puramente localmente. Con e-TeX tenemos un _mucho_ más registros, por lo que este enfoque es menos necesario: podemos asignar libremente más registros (y macros) para propósitos dedicados. En particular, significa que no estamos tan presionados para 'reciclar' registros con varios nombres.

El código base de expl3 ha estado en desarrollo durante _mucho tiempo_ y originalmente no requería e-TeX. El desarrollo de expl3 también se realiza principalmente 'encima' de LaTeX2e, con un principio general de que expl3 no contamina el espacio de nombres del documento ni cambia los comportamientos centrales de LaTeX2e.

En particular, debemos tener en cuenta que \newcount LaTeX2e es similar a plain: asigna globalmente y no verifica. Así en particular

\def\foo{%
  \begingroup
    \newcount\localfoocnt

es 'mal estilo' como si \foo se llamara varias veces, entonces usaremos registros que nunca se liberan.

Por expl3 , el equipo ha intentado evitar vincular el comportamiento documentado de las variables a la implementación de macros o registros. (Observo que permitimos el uso de tl sin un descriptor de acceso, que en última instancia depende de que sean macros). También seguimos haciendo uso de registros para enteros, dimens, _etc._ ya que están disponibles y ofrecen mejor rendimiento y "auto-terminación" que hacer todo en macros. (Eso sería factible usando e-TeX y las diversas primitivas \<thing>expr .) Como tal, necesitamos tener un sistema de asignación, y para ser consistentes asignamos todos los tipos de variables, no solo aquellas que se basan en registros. . Como ha observado @EvanAad , cuando la verificación no está activa, se puede hacer _e.g._ \tl_set_eq:NN \l_new_tl \l_existing_tl sin error, pero este no es el comportamiento admitido (la verificación lo marcará, por ejemplo).

Como parte del asignador expl3 , se tomó la decisión de que new _verificaría_ la existencia, en contraste con \newcount , por lo que

\cs_new_protected:Npn \my_foo:
  {
    \group_begin:
      \tl_new:N \l_my_tl

generará un _error_ si \my_foo: se usa repetidamente. Esto empuja a uno en la dirección que ha sido la práctica estándar de TeX desde los primeros días de simple: las asignaciones salen de la macro. (Tenga en cuenta que, en términos simples, \newcount es \outer por lo que está 'prohibido' dentro de las macros).

\tl_new:N \l_my_tl
\cs_new_protected:Npn \my_foo:
  {
    \group_begin:
      \tl_clear:N \l_my_tl

El paquete etex introdujo hace muchos años un asignador de registros que puede realizar la asignación de registros tanto local como global, \loccount _versus_ \globcount , _etc. Durante bastante tiempo, expl3 cargó etex y se usó para proporcionar funcionalidad para \int_local_new:N y similares. Dado el hecho de que la mayoría de los programadores de TeX están acostumbrados a realizar asignaciones globales, tal vez no sea sorprendente que esto no se haya adoptado tan ampliamente. Sin embargo, el equipo también estaba preocupado por posibles malentendidos. Con algo como

\cs_new_protected:Npn \my_foo:
  {
    \group_begin:
      \tl_local_new:N \l_my_tl

tenemos la pregunta de qué sucede en las llamadas anidadas a \my_foo: , o lugares más probables donde se va a usar algún nombre 'simple' como \l_my_tmp_tl . Basado en la idea general de que queremos que new haga comprobaciones, eso debería ser un error. Los programadores que vienen de muchos otros lenguajes pueden encontrar eso algo extraño. Por supuesto, encaja con las reglas de alcance de TeX.

En particular, los cambios en el kernel de LaTe2e en los últimos años significan que ya no querríamos cargar etex (de hecho, hemos trabajado duro para hacer expl3 'autónomos'). Por lo tanto, cualquier asignador local nuevo tendría que escribirse para que funcione con LaTeX2e sin comportamientos 'perturbadores' para aquellos que no usan expl3 : factible pero no del todo trivial.

Por algo como \l_my_tmp_tl uno podría, por supuesto, hacer una asignación global, pero luego uno tiene que saber qué variables \l_... están asignadas localmente en el grupo actual y cuáles están asignadas globalmente pero asignadas localmente.

No tengo datos de prueba a mano, pero probablemente vale la pena señalar que asignar una variable es más trabajo que simplemente configurarla (ciertamente cuando la verificación no está activa), por lo que usar un asignador local sería un poco más lento que un asignador global más asignaciones locales.

Por lo tanto, por una combinación de razones técnicas y "encajar con la historia", decidimos ceñirnos a la _asignación_ estrictamente global. Esto no impide de ninguna manera la asignación local de variables, que se recomiendan y se utilizan ampliamente.

Eso nos lleva al elemento 'social'. La captación de expl3 en los últimos años se ha visto fuertemente favorecida al hacer que las piezas sean 'ampliamente' estables. En algún momento, el equipo tiene que tomar una decisión sobre las cosas, en parte para que la gente pueda usarlas y en parte para que pasemos a otras tareas. Eso no nos impide volver a visitar las cosas, pero las convenciones más establecidas necesitan una buena razón para ser modificadas.

Aquí, en la actualidad, supongo que no veo qué está mal con el enfoque 'habitual' de

\tl_new:N \l_my_tl
\cs_new_protected:Npn \my_foo:
  {
    \group_begin:
      \tl_clear:N \l_my_tl

al menos hasta el punto en que se requiere que retrocedamos y cambiemos la configuración actual.

@blefloch Sí, estoy de acuerdo en que mi reacción cuando @wspr cerró el hilo no fue razonable y me disculpo con él y con el resto de ustedes. Sentí que me estaban despidiendo y silenciado, y reaccioné con una rabieta. Esto es inaceptable. Lo siento por eso.

@josephwright gracias por la respuesta detallada. Después de pensar un poco en el asunto, me di cuenta de que si solo uso \<module>_clear_new:N , entonces, en efecto, todas mis variables se comportarán como si fueran locales del grupo circundante, siempre y cuando asigne usando \<module>_set... lugar de \<module>_gset... . Además, creo que esta práctica está de acuerdo con la convención de LaTeX3 de declarar una variable antes de su uso, por lo que no me marcarán si las comprobaciones están activadas.

Siguiendo esta práctica, reescribiría el último ejemplo de @josephwright así:

\cs_new_protected:Npn \my_foo:
{
    \group_begin:
        \tl_clear_new:N \l_my_tl

El único tipo de datos que no se ajusta a este esquema es cs , pero hasta donde yo sé, es legal crear variables de este tipo con \cs_set:Npn et al. sin antes declararlos, porque LaTeX3 realmente no trata cs como un tipo de datos normal a la par con los demás (que es un tema separado que me gustaría abordar con ustedes, pero dejaré a otro hilo). Con este enfoque, las variables cs también se pueden crear localmente. Si quiero mantener la sintaxis consistente, siempre puedo escribir mi propio contenedor \cs_clear_new:N .

Entonces, en lo que a mí respecta, ahora puede cerrar este problema si lo desea, @wspr .

@EvanAad En \<thing>_clear_new:N , observe que la declaración _ es_ global donde la variable aún no existe. Entonces

\cs_new_protected:Npn \my_foo:
{
    \group_begin:
        \tl_clear_new:N \l_my_tl
    \group_end:
}
\my_foo:
\tl_show:N \l_my_tl

mostrará que \l_my_tl está definido y vacío, suponiendo que no lo haya configurado previamente en otra cosa.

Me gustaría unirme a @blefloch con la esperanza de que esta discusión conduzca a una mejor documentación. En particular, creo que cuando escribe que el programador "debe" seguir un cierto patrón de codificación, como en las oraciones que cité en mi publicación original, la documentación debería dilucidar lo que este "debe" significar. ¿Qué seguirá si no se cumple el patrón?

  1. ¿Será indefinido el comportamiento del idioma?
  2. ¿Se admite actualmente la no adherencia, pero es posible que no lo sea en versiones futuras?
  3. ¿El motor informará de un error?
  4. ¿El motor informará un error, pero solo cuando las comprobaciones estén activadas?
  5. ¿Se producirán algunos inconvenientes menores?
  6. ¿El equipo de LaTeX3 se quejará de descontento, pero no se producirá ningún error o pérdida de funcionalidad?

Como ejemplo para 5, tome la convención de agregar especificaciones de argumentos al final del nombre de una función. Por lo que puedo decir, la única función que no funcionará correctamente si no se cumple esta convención es \cs_new:Nn , pero el resto, incluido `cs_ new: Npn 'funcionará perfectamente bien.

Como ejemplo para 6, tome la convención de marcar variables locales y globales con \g_... y \l_... . Hasta donde yo sé, no habrá absolutamente ninguna repercusión por no seguir esta convención. Todo funcionará correctamente.

@EvanAad - disculpa aceptada, y lamento de mi parte por cerrar el tema antes de que se completara la discusión.

@josephwright - como una pequeña ventaja de los beneficios para la asignación local, si escribo

\cs_new_protected:Npn \my_foo:
  {
    \group_begin:
      \tl_new_local:N \l_my_tl
      ...

entonces sé que \l_my_tl no solo está libre de interferencias desde el exterior, sino que, a diferencia del uso de una función _clear , sé que todo rastro de la variable se ha ido fuera del uso de la función. En otras palabras, con solo mirar el código, sé que no se puede usar como una variable semi-global en el \tl_if_exist_p:N ).

(Debe ejecutarse, pero puede continuar más tarde si tiene algún sentido seguir discutiendo).

@wspr Sí, pero eso es todo con el hecho de que se basa en el alcance del grupo TeX. Por lo tanto, se puede usar 'semi-globalmente' en una construcción de la forma

\cs_new_protected:Npn \my_foo:
  {
    \group_begin:
      \tl_new_local:N \l_my_tl
      \tl_set:Nn \l_my_tl { foo }
      \__my_foo:
     ..
  }
\cs_new_protected:Npn \__my_foo:
  {
    \group_begin:
        \tl_use:N \l_my_foo % Definition from \my_foo: => "foo"
...
  }

que era al menos parte de nuestro pensamiento.

Creo que antes de dejar esto, me gustaría hacer hincapié en que, a nivel técnico, hay varias formas de establecer un asignador de registros local.

@josephwright - Sin qué \__my_foo: heredaría el alcance de la función "externa"? Es perfectamente coherente con el comportamiento de agrupación de TeX.

(Lamento mantener la discusión. Ha sido un día largo. ¿Hay alguien interesado en continuar?)

Soy el 19.10.2017 um 09:23 schrieb Joseph Wright:

Creo que antes de dejar esto me gustaría enfatizar que en un
nivel técnico hay varias formas de crear un registro local
asignador.

sí, pero bastante cerca del argumento de Turing, es decir, cualquier implementación de este tipo
va a ser muy ineficiente en tiempo de ejecución porque el motor subyacente
está gestionando el almacenamiento de registros a nivel mundial. Y expl3 (en ese nivel)
debe permanecer "razonablemente eficiente.

eso es como, digamos, variables globales y locales y su mutador. Bastante
que probar en cada función si está haciendo una operación global en un
variable local, el concepto está por defecto solo presente en los nombres,
por ejemplo, \ l _... se supone que es una variable local y no debe usarse
con una función global como ..._ gset: Nn pero no estamos probando eso en
tiempo de ejecución

Sin embargo, ofrecemos un módulo de verificación (que se ejecuta cuántas veces más lento)
eso asegura que todas estas convenciones sean realmente obedecidas.

Entonces, para volver a las variables globales / locales

el concepto estándar de expl3 es

  • declarar el nombre de una variable una vez (globalmente) - muchas porque en
    al menos algunos de los tipos tienen solo contenedores de almacenamiento globales

  • use la convención de nombres \ l_ \ g_ para denotar variables locales y globales

que te ofrece tanto globales como locales, pero tiene las restricciones que

  • no declaras una variable local al principio de su alcance,
    en su lugar, usa _set o _clear_new en ese punto y el último
    puede significar que el nombre de la variable puede venir en ese punto globalmente
    en existencia

  • fuera del alcance, la variable todavía existe con el valor predeterminado
    valor del tipo (por ejemplo, _int es 0, etc.) para que no obtenga un compilador
    error si se refiere a una variable de este tipo "por error" fuera de su
    alcance

Básicamente, lo único que no obtienes es poder declarar un
variable local para que su nombre desaparezca fuera del alcance (y produzca
un error indefinido de algún tipo en tiempo de ejecución si se hace referencia fuera del
alcance declarado).

Para hacer eso (que sí es posible) expl3 necesitaría mantener su propio
conjunto de recursos mucho más allá de una simple asociación entre un nombre y un global
grupo ofrecido por el motor y eso significaría que todos los accesos serían
muy notablemente ralentizado hecho, lo que va en contra de los criterios de diseño de expl3.

@FrankMittelbach - ahora me tienes curiosidad; ¿Es el enfoque etex.sty para registros realmente tan ineficiente? O tal vez te refieres a las variables tl, ¡en cuyo caso estoy de acuerdo!

@FrankMittelbach En mi opinión es importante distinguir, y dejar claro en la documentación, qué es una buena práctica según el equipo de LaTeX3 vs. qué es un requisito formal, ya sea gramatical o semántico, de las reglas del lenguaje LaTeX3.

Las dos reglas que citó, a saber:

  • declarar el nombre de una variable una vez (globalmente)
  • use la convención de nombres \l_ \g_ para denotar variables locales y globales

caen bajo esta rúbrica de convenciones de codificación que el equipo de LaTeX3 considera aconsejables, pero ninguna de ellas está obligada por las reglas gramaticales del lenguaje, y no adherirse a estas convenciones no causa ningún error o pérdida de funcionalidad.

En otras palabras, seguir estas reglas es una cuestión de gusto personal y estilo de codificación, y esto debería quedar claro en la documentación, en mi opinión.

eso es un poco como decir que la señal 50mh no es una regla sino una conducción
convención y es una cuestión de gustos si un conductor se adhiere a ella (solo
porque no se comprueba inmediatamente la mayor parte del tiempo)

si usa una variable \ l_ con _gset, entonces todavía programa en TeX pero
has dejado de obedecer las reglas gramaticales del expl3
idioma. ¿Rompe inmediatamente tu código? probablemente no, pero tu
generar acumulación de savestack (ver índice de TeXbook)

¿Estás diciendo que es solo una regla gramatical si la estamos comprobando en
en tiempo de ejecución, digamos, cada martes?

@FrankMittelbach Claro, si define esto como parte de las reglas del lenguaje, entonces lo es por definición, pero mi punto es que esto no debe definirse como parte de las reglas del lenguaje, porque nunca se verifica, y porque no adherirse a esta convención no causa en sí mismo un error o pérdida de funcionalidad.

Es la diferencia entre decir "Cuando escribes en inglés es mejor sostener el bolígrafo con la mano derecha porque esto evita manchar la tinta". y aprobar una ley que dice que el inglés debe escribirse con la pluma en la mano derecha. Claro, puede aprobar una ley de este tipo, y hay una razón sólida para justificarla, pero en última instancia, la elección en qué mano sostener la pluma debe dejarse al escritor individual. Y habrá personas que elegirán no adherirse a esta regla y terminarán escribiendo tan ordenadamente como aquellos que se adhieran a ella.

Creo que @FrankMittelbach exagera la sobrecarga de las declaraciones locales ( \loccount administra los recuentos localmente, nada de lo que hace es global, por lo que mantiene la sobrecarga bien).

No veo ningún problema importante al proporcionar un \int_local:N y \tl_local:N (= \tl_set_eq:NN #1 \c_empty_tl ) etc.que sería muy análogo a \int_zero_new:N y \tl_clear_new:N pero solo haría "nuevo" localmente. Esto requiere un poco de trabajo para los registros, pero no excesivamente.

@EvanAad debe saber que no declarar una variable puede morderlo en algún momento (advertencia: este código produce infinitas páginas).

\documentclass{article}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn
\tl_put_left:cn { l_my_tl } { foobar \par }
\l_my_tl
\end{document}

@blefloch Lo que estoy diciendo es que está perfectamente bien declarar una variable dentro de una macro (con \<module>_clear_new ), y no tiene que nombrarla \l_amount_paid_int , simplemente puede llamar it \amount_paid o \amountPaid , como lo haría en cualquier otro lenguaje de programación. El \l_..._int es un mnemónico agradable para recordarle que es un número entero y que se supone que debe usarse localmente, pero no debería verse obligado a usar este o cualquier otro mnemónico.

porque nunca se marca y porque no se adhiere a esta convención
en sí mismo no causa un error o pérdida de funcionalidad.

pero ese es el punto

a) causa daño dependiendo de las circunstancias, es decir, cuando se mezcla
asignaciones globales y locales a la misma variable

b) verificamos a pedido (y en este momento ese código puede no ser
funcional pero lo fue y probablemente volverá a serlo eventualmente debido a a))

ps sí, entiendo su punto sobre sostener el bolígrafo (ser zurdo) y sí, yo
he estado por encima del límite de velocidad (en mi bicicleta) pero aún considero esto un
La regla de tráfico no es una convención de tráfico y sí, uno puede desobedecer eso.
sin daño, pero también uno puede morir por ello o al menos terminar con una multa

(Lo que mi ejemplo de código anterior estaba señalando es que incluso para tl es esencial declararlos antes de usarlos. Hay una opción de verificación que prueba las declaraciones, pero en el uso normal no queremos tales gastos generales).

Estoy de acuerdo con @EvanAad en que los nombres son solo una convención. Podemos comprobar que las asignaciones locales y globales no se mezclan incluso sin la convención de nombres l_ / g_: dado que se permite que el código de verificación sea algo lento, es bastante razonable almacenar la información sobre si una variable dada se usó en un local / global asignación. Para los tipos de verificación, la situación es similar, excepto por dos pares de tipos que no se pueden distinguir (no voy a decir cuál para evitar descarrilar la conversación).

En las 'convenciones', \amountPaid es un comando de documento mientras que \l_amount_paid_int no lo es, y este último es parte del espacio amount nombres \l_ / \g_ , aunque creo que muchos idiomas tienen un fuerte "rumbo" en la denominación sin que se aplique a nivel técnico.

El 19 de octubre de 2017, a las 16:58, Joseph Wright [email protected] escribió:

En las 'convenciones', amountPaid es un comando de documento, mientras que \ l_amount_paid_int no lo es, y este último es parte del espacio de nombres de la cantidad (por convención, pero muy importante en TeX). Eso no se aplica a \ l _ / \ g_, aunque creo que muchos idiomas tienen un fuerte "rumbo" en la denominación sin que se aplique a nivel técnico.

La peor pesadilla en la programación LaTeX2e es definir un comando
en el "espacio de nivel de usuario", digamos \ foo, para descubrir que choca
con un comando otro paquete definido para uso _internal_ (por lo que
no documentado en el manual).

¿Realmente sucedió? Sí, y no solo una vez. Cumpliendo con el
\La convención
tales problemas.

Por supuesto, también pueden producirse conflictos en el "espacio de nivel de usuario",
pero son mucho más fáciles de detectar y resolver. Cuando se trata de
internos, a menudo es necesario perseguir expansiones en un
Nivel profundo.

A diferencia de los legisladores, no podemos imponer multas o encarcelar
que no se adhiere a las leyes de la programación LaTeX3. Pero dónde
una comunidad y todo el mundo debería.

Nuestras pautas sobre convenciones de nomenclatura deberían ayudar a que nunca se encuentre
los comandos internos de un paquete para chocar con otros.

Para el código personal, uno tiene derecho a hacer lo que quiera: hay
ninguna ley que prohíba que uno se apresure en su propiedad privada, pero
hay uno sobre hacer eso en una vía pública.

Si desea definir localmente la variable \ f de cualquier tipo,
deberías preocuparte de que el comando sea definido por algunos
paquete y, según la ley de Murphy, termina exactamente en el argumento
a una función usando la variable \ f. ¿Puedes imaginar algo peor?
¿guión?

Ciao
Enrico

y no tiene que nombrarlo \ l_amount_paid_int, simplemente puede llamarlo amount_paid como lo haría en cualquier otro lenguaje de programación. El \ l _... es un mnemónico agradable para recordarle que se supone que debe usarse localmente, pero no tiene que usar este ni ningún otro mnemónico.

Seguro. También puede usar md5-fc693aa157832059d7daeeb61c55 cddb: pagado o monto y pago (eso no es una broma, conozco un paquete que usa &) o lo que prefiera. Pero incluso si los nombres son solo una convención: la comunicación es más fácil si las personas se adhieren a tales convenciones. Ha estado haciendo muchas preguntas en tex.sx. ¿Qué hará si tiene un problema con su código escrito en "estilo personal"? ¿Traducirlo al estilo estándar, hacer una pregunta y traducirlo de nuevo? ¿O espera que todos puedan manejar su estilo personal?

@ eg9

Para el código personal, uno tiene derecho a hacer lo que quiera

No lo sabría leyendo la documentación, es todo lo que estoy diciendo.

Si desea definir localmente la variable \f de cualquier tipo, debería preocuparse de que el comando esté definido por algún paquete

Estoy en desacuerdo. Si su código está dentro de un \group_begin: ... \group_end: , y si define todas las variables locales con \<module>_clear_new:N , y si solo asigna a variables locales con \<module>_set:N... , no necesita preocuparse por el nombre de sus variables locales choca con otros paquetes, a menos que su código use otro paquete.

@ u-fischer

Pero incluso si los nombres son solo una convención: la comunicación es más fácil si las personas se adhieren a tales convenciones.

No digo que no haya buenas razones para ceñirse a las convenciones. Todo lo que digo es que estas convenciones no deben convertirse en reglas de lenguaje, y la documentación debe diferenciar claramente entre las convenciones, para las cuales se debe establecer la justificación, y las reglas. Y la decisión de seguir las convenciones debe recaer en última instancia en el programador.

@EvanAad Debe preocuparse por los conflictos de nombres incluso en el caso que describe. Di que escribes

\cs_new_protected:Npn \evanaad_halve:n #1
  {
    \group_begin:
      \int_zero_new:N \f
      \int_set:Nn \f { (#1) / 2 }
      \iow_term:x { \int_use:N \f }
    \group_end:
  }

entonces un usuario de su paquete lo hace

 \int_const:Nn \f {123}
 \evenaad_halve:n { \f }

Se sorprenderán al ver 0 y no 62.

Por otro lado, si te quedas con nombres como \evanaad_f etc (o más cortos \@@_f etc usando l3docstrip magic) deberías estar seguro.

No lo sabría leyendo la documentación, es todo lo que estoy diciendo.

Lo siento, pero expl3.pdf usa la palabra "convención" unas 20 veces. En el caso del comando público versus privado, incluso existe la oración "No hay (casi) forma de hacer cumplir esto sin una sobrecarga informática severa, por lo que lo implementamos solo a través de una convención de nomenclatura".

@blefloch Buen punto.

@ u-fischer Está bien, es justo. ¿Qué pasa con la convención de que el nombre de una función debe terminar con un especificador de argumento? Esto no es completamente una convención, porque las funciones de la sección 3.3 inspeccionan el especificador de argumentos, pero estas funciones son simplemente "azúcar sintáctico", y si no las usa, no hay ningún obstáculo para usar nombres de funciones como \mymodule_myfunc , pero no lo sabría del manual.

El 19 de octubre de 2017, a las 17:52, EvanAad [email protected] escribió:

@ u-fischer Está bien, es justo. ¿Qué pasa con la convención de que los nombres de las funciones deben terminar con un especificador de argumento? Esto no es completamente una convención, porque las funciones de la sección 3.3 inspeccionan el especificador de argumento, pero estas funciones son simplemente "azúcar sintáctico", y si no las usa, no hay ningún obstáculo al usar nombres de funciones como \ mymodule_myfunc.

Eso es necesario para cs_generate_ variant: Nn , por supuesto.

Ciao
Enrico

Con \mymodule_myfunc no podrá utilizar ninguna función de
l3expan . Estas funciones de expansión, y la noción de variantes, son una
parte central de expl3.

Si bien estoy de acuerdo en que los nombres de las variables podrían hacerse más cortos (eliminando
"l _" / "g_" y "_int" / ...), la firma de la función no es realmente "solo un
convención".

@blefloch Veo tu punto, y es bueno, pero aún así, en mi opinión, si todo lo que quieres es escribir un comando de documento, debes saber que puedes hacerlo definiendo una función como

\ExplSyntaxOn
\cs_new:Npn \MyDocumentCommand {Hello,~world!}
\ExplSyntaxOff

y no tienes que definir primero una función "sombra" \mymodule_my_document_command: , y luego copiarla con

\cs_new_eq:NN \MyDocumentCommand \mymodule_my_document_command:

@blefloch Y, por cierto, además de \cs_generate_variant:Nn , que mencionó @ eg9 , ¿hay otras funciones del módulo l3expan que utilicen la parte del especificador de argumento del nombre de la función?

a estas alturas me parece que estás discutiendo en gran medida por el simple hecho de discutir

sí, puedes hacer todo esto y al final del día el único lenguaje difícil
Las reglas son lo que el motor TeX fija en sus primitivas. Y dado
que TeX es un lenguaje auto modificable al que puede ir básicamente desde cualquier lugar
allí, por ejemplo

\ endlinechar-1 \ def ~ # 1 {\ catcode` # 113} ~ Q ~ S ~ U ~ _ ~ V ~ W ~ J ~ K ~ L ~ M ~ N ~ O ~ @ ~ X ~ Y ~ [~] ~ (
~ | ~ & ~ Z ~ '~ "~ ~ h ~ z ~: ~ q ~ j ~ k ~; ~ / ~) ~! ~, ~ $ ~ + \ Let_ \ let_ ~ newcount ~ $$ - 1 ~ Q ~ J ~ V ~ S~ K ~ W ~ U ~ L ~, ~ '' 1 ~ "" 2 ~ * 1 _ & \ count & 144 '& 155' & 145 "& 154" _ [\ ifnum _ (\ ifcase_O \ o
_ | \ else _] \ fi_N \ number _ @ \ advance_X \ expandafter_Z \ global_Y \ typeout_ ~ newif
~ \ ifG ~ \ if_ ~ \ def ~ j {[0 Q [0Jk | $] | $] | $] | $]} ~ k {& 1NQNJ} ~ \ 2 # 1 # 2 {} ~: # 1 {

11 # 12 # 13 # 14 # 15 # 16 # 17 # 18} ~ h # 1 # 2 {# 2: {~ \ q # 1} ~ # 2 ^^ J} ~ \ q # 1 # 2 {(& 1 # 1 # 2 ~~ OO $]}

~ / {Y {¿Fila y columna? por ejemplo, E6} \ read $ toM \ ifcat ~ X \ 2M ~ $$ X \ jM |! input!]} ~! # 1! {
Y {No válido # 1.} /} ~ \ J # 1 # 2 {Q #1@Q- @J #2@J- 0; (¡V! Mover!]} ~; {V0 (jS1z1z0z {$} S
0z1z {$} S $ z1z0z {$}]} ~ _ {@, \ ifodd '-]} ~ z # 1 {{\ trueK # 1 {\ falseq}}} ~ q {@ QS @ JK [j = "
\ ifZk'Z_2] @ V1q | [j = 'ZVV \ ifG \ if | \ aftergroupq]]]]} ~ \, # 1 {Q # 1:.} ~. # 1 {J # 1; [0
WWVUQLJ]]} ~ + # 1 {(# 1O2O-2O0O0O0O0O-2O2]} ~) {'X "X" N'Y {^^ J:
~ ^^ Jh1Ah2Bh3Ch4Dh5Eh6Fh7Gh8H: ~ ^^ J} \ GfalseW (W $ | 0] ~: \, \ Gtrue [0 /]; k'_1] [$ = WY {(, Empate | Jugador [0> ,. | $] ~ gana por N [0>, -],].} X \ dump])} ~~ {})

que es un hermoso documento TeX (de hecho, un hermoso documento LaTeX)
pero en lo que respecta a su código, no es en absoluto muy útil. Y
El hecho de que Bruno sea capaz de escribir un documento así no significa que
el manual expl3 (o en este caso un manual de LaTeX) debería describir cualquiera de ellos.

El código LaTeX (2.09 y también 2e) tuvo el gran problema de que en los primeros días
demasiada gente que programaba sí entendía acerca de los atajos de bajo nivel
en TeX y los usó y abusó de ellos porque pensaron que no es
dañino. Como resultado, una gran cantidad de paquetes 2e existentes se
incompatibles entre sí o tienen problemas sutiles en algunos lugares cuando se usan
juntos, etc. o se rompen de vez en cuando porque pasaron por alto uno o el
interfaz de usuario (porque parecía funcionar sin ella).

Básicamente, nos está pidiendo una y otra vez que documentemos solo esto,
es decir, posibles atajos que violan los principios de diseño
porque a veces funcionan (o incluso en el momento siempre). Pero expl3
y sus convenciones / reglas se derivan en gran medida de experimentar que
Los codificadores desobedecieron tales reglas en el pasado y el desorden que es el resultado
de esto. Así que no, las reglas son deliberadas y no meros caprichos (la mayoría
del tiempo) y aunque "si sabe lo que está haciendo, entonces
puede evitar la mayoría de ellos en una situación específica "eso no significa que
si mueve su código, entonces de un lugar a otro que seguirá siendo
el caso o debe ser el caso a lo largo del tiempo.

Como han dicho otros, hay una gran diferencia entre el código en el que escribe
la marcha por ti mismo y el código que escribes como distribuido "oficialmente"
paquete. Al menos para esto último, pedimos aceptar las reglas y
considérelos parte del idioma. Por ti mismo, es posible que desees
aprender a codificar un documento como el anterior, pero el conocimiento de cómo hacerlo
que no saldrá del manual expl3


Habiendo dicho eso, no quiero desanimarlos desafiando conceptos,
comandos, interfaces, lo que tengas. Muchos de los puntos que planteaste
otras ocasiones han sido bien aprovechadas (o al menos nos han hecho repensar una
o el otro punto).

Pero en lo que respecta al manual expl3, creo que lo que escuchaste de
varias personas es que no hay interés en documentar la
las "no convenciones" y las "no reglas". Además, si el código se desvía demasiado
de lo que llamamos reglas / convenciones, entonces seguirá siendo código TeX pero
ya no es código expl3.

Creo que hemos hablado de esto: cerraré pero, por supuesto, volveré a abrir si se solicita.

¿Fue útil esta página
0 / 5 - 0 calificaciones