Saltar al contenido

Ventajas y desventajas de las macros completamente expandibles

Solución:

Creo que es mejor no comparar la distinción expandible / no expandible con conceptos de otros lenguajes. Los principales problemas relacionados con la expansión son realmente particulares (algunos dirían que peculiares) del modelo de ejecución de TeX.

TeX tiene dos modos principales de funcionamiento. Todas las asignaciones y operaciones de boxeo ocurren (en “el estómago” en El TeXBook terminología) como operaciones no ampliables. La expansión macro ocurre antes de eso, pero a diferencia de (digamos) la expansión macro del preprocesador C, la expansión macro y las operaciones no expandibles están necesariamente entrelazadas.

Probablemente valga la pena señalar que la pregunta tal como se plantea no está bien definida.

Los tokens TeX son ampliable o no expandible pero “completamente expandible” es un área gris llena de trampas en las que los incautos pueden caer.

Cualquier token definido por def (o newcommand etc) es por definición ampliable.

Una ficha de personaje como a es, por definición, no expandible.

def es un token no expandible.

Así que si defines

defzza{}
defzzb{a}
defzzc{defzze{}}
defzzd{ifmmode a else bfi}

entonces cada uno de estos es ampliable, con expansión <nothing> a defzze{} ifmmode a else bfi respectivamente.

Sin embargo, ¿cuál de estos es completamente expandible ?

Claramente zza es. Pero si la definición de “completamente expandible” significa se puede expandir repetidamente sin dejar tokens no expandibles entonces los únicos tokens completamente expandibles se expandirán a nada.

Entonces la mayoría de la gente clasificaría zzb como completamente expandible, a pesar de que se expande a a que no es ampliable.

Por lo tanto, un término mejor (o al menos más preciso) que “completamente expandible” es “seguro en un contexto de solo expansión”. Dentro edef y write y cuando TeX busca un número o dimensión, y algunos otros lugares, TeX solamente hace expansión y no realiza ninguna asignación u otras operaciones no expandibles.

 edefyyb{zzb}

es por supuesto seguro, es lo mismo que defyyb{a}. Entonces yyb es seguro en un contexto de solo expansión.

edefyyc{zzc}

no es seguro, es lo mismo que

edefyyc{defzze{}}

Ahora def no se expande, pero en un contexto de solo expansión, el token permanece inerte, no hace una definición, por lo que TeX intenta expandirse zze que normalmente aún no está definido, por lo que esto conduce a un error, o si zze tiene una definición, entonces esto se ampliará, lo que casi siempre es un comportamiento no deseado. Esta es la causa básica de los infames errores de “comando frágil en un argumento en movimiento” en LaTeX.

Entonces zzc no es seguro en un contexto de solo expansión. Si hubiera sido definido por la construcción e-TeX

protecteddefzzc{defzze{}}

Luego, en una construcción de solo expansión, los tokens protegidos se hacen no expandibles, por lo que

edefyyc{zzc}

entonces estaría a salvo, y lo mismo que defyyc{zzc} Por lo tanto, un comando protegido es seguro en un contexto de solo expansión, pero dado que esta seguridad viene al hacer que el token no se pueda expandir temporalmente, probablemente no sea exacto decir que es “completamente expandible”.

edefyyd{zzd}

es

edefyyd{ifmmode a else bfi}

cual es

defyyd{b}

o defyyd{a} si la definicion esta pasando por dentro $...$ (o una pantalla de ecuación). Del mismo modo, se expandirá a b al comienzo de una celda de matriz, ya que la expansión ocurrirá mientras TeX se expande buscando omit (multicolumn) y así antes de que haya insertado el $ para poner la celda de matriz en modo matemático. Una vez más, lo que se requiere aquí es una definición protegida para limitar la expansión.

Entonces, a veces es bueno hacer que las cosas se puedan expandir, ya que mantiene más opciones abiertas.

deftesta#1#2#3{%
  ifnum#1=0
  defnext{#2}%
  else
  defnext{#3}%
  fi
  next}

deffirstoftwo#1#2{#1}
defsecondoftwo#1#2{#2}

deftestb#1{%
  ifnum#1=0
  expandafterfirstoftwo
  else
  expandaftersecondoftwo
  fi}

ambos testa{n}{yes}{no} y testb{n}{yes}{no} ejecutará yes
si n es 0 y no de lo contrario pero testb funciona por expansión y, por lo tanto, es seguro en un contexto de solo expansión (si sus argumentos son seguros). los testa La versión se basa en el funcionamiento interno no ampliable de defnext. (Plain TeX y LaTeX2.09 usan muchas pruebas usando defnext, LaTeX2e los cambió a la forma expandible cuando fue posible).

Para una prueba numérica, es fácil usar la forma expandible, pero si desea probar si dos “cadenas” son iguales, la forma más fácil es ir

deftestc#1#2{%
  deftempa{#1}deftempb{#2}%
  ifxtempatempb
  expandafterfirstoftwo
  else
  expandaftersecondoftwo
  fi}

pero ahora, aunque hemos usado el expandafterfirstoftwo
constructo, la prueba se basa en dos definiciones no expandibles. Si tu
De Verdad necesita probar de una manera ampliable, puede encontrar algunas preguntas en este sitio, pero cualquier respuesta suele estar llena de condiciones especiales y casos en los que no funciona y se basa en algún tipo de bucle lento token por token a través de los dos argumentos probando si son iguales. En el 99% de los casos, esta complicación simplemente no es necesaria y la prueba no expansible es suficiente. Si está intentando definir un conjunto coherente de pruebas (como en el ifthen paquete ifthenelse por ejemplo, si se resigna al hecho de que algunas pruebas son necesariamente no expandibles, puede optar por hacer que todas no sean expandibles para que se comporten de manera coherente.

Entonces la respuesta es:

Todo depende….

Si bien TeX es Turing completo, esto no dice nada sobre si una operación en particular se puede llevar a cabo de manera expandible. Por lo tanto, el primer problema con el objetivo al considerar si algo se puede codificar de manera expandible es si es posible. Como se discutió en ¿Por qué no es expandible todo ?, algunas operaciones no son expandibles como no lo son las primitivas subyacentes. Los casos obvios incluyen asignaciones, agrupaciones y cualquier forma de composición tipográfica en cajas. Esto significa que, por ejemplo, no es posible medir el ancho de la composición tipográfica de un material de manera expandible, por lo que no puede hacer lo que podría intentar con algo como

newdimenmydim
mydimwdhbox{foo}%

a pesar de que esto parece ‘lógico’, y en su lugar tiene que adoptar un enfoque equivalente a

newdimenmydim
newboxmybox
setboxmybox=hbox{foo}%
mydimwdmybox

(Esto se muestra más claramente cuando comienza a querer hacer expresiones más complejas).

La segunda cuestión, aludida en el xparse manual, es que a menudo existen limitaciones para los métodos expandibles que no aparecen en los no expandibles. Un caso que es particularmente notable es el de buscar argumentos opcionales en el futuro. De manera no expandible, esto se hace usando futurelet (una asignación, por lo que no se puede ampliar). Para hacer lo mismo de manera expansible, debes tomar una discusión. Ese método no es tan robusto, como si tuviéramos

foo{[} ...

luego futurelet detectará {, mientras que la captura de argumentos verá [. Suponiendo que estamos buscando un argumento opcional similar a LaTeX, la prueba expandible será ‘incorrecta’. Entonces, el código expandible aquí es más limitado en términos de lo que puede manejar.

Escribir código expandible suele ser mucho más complicado que el caso no expandible. No puede guardar nada más que dejarlo en el flujo de entrada de una manera conocida, lo que puede ser muy difícil de rastrear. Esto también puede tener implicaciones en el rendimiento, ya que para una operación compleja es posible que deba realizar un seguimiento de una gran cantidad de cosas.

Un problema relacionado aquí es que hay dos tipos de capacidad de expansión. Dentro de un edef, TeX seguirá expandiendo tokens hasta que solo queden los no expandibles. En este caso, puede agregar tokens de ‘salida’ al principio del flujo de entrada y hacer que TeX continúe procesando el resto de la operación. Por otro lado, puede hacer una expansión expandible usando romannumeral-`qSin embargo, esto se detiene en el primer token no expandible. Entonces, si desea escribir una función que funcione dentro de esta expansión ‘completa’, debe observar los tokens de salida con mucho cuidado. (El LaTeX3 expl3 El lenguaje diferencia entre estas dos formas de expansión como x– y f-type, respectivamente, y la documentación indica si las funciones expandibles funcionarán correctamente dentro f-tipo situaciones.)

Esto puede hacer que se pregunte por qué escribiría funciones expandibles. Como señala la pregunta, existen ventajas. La más obvia es que se pueden usar dentro de las asignaciones, como establecer dimensiones. Bruno Le Floch ha escrito una FPU expandible para LaTeX3, que sería un gran ejemplo de esto. TeX también expande tokens en algunos otros contextos, más obviamente al comienzo de una tabla (halign) celda para buscar omit o noalign, y allí debe usar métodos expandibles si desea lidiar (fácilmente) con el escape de la alineación. (¡Hay otros enfoques para esto!)

Cuando TeX escribe en archivos, realiza una expansión completa. Esto puede ser deseable o no con una macro determinada. Por ejemplo, muchas macros de LaTeX se escriben sin expansión al .aux archivo, ya que es el momento “incorrecto” para expandir.

En general, con la excepción de las capas de programación (donde desea capacidad de expansión si es posible), la creación de macros expandibles no suele ser una prioridad, de ahí la declaración en xparse. En términos generales, buscaría crear funciones expandibles cuando sé que se necesita una.

Quizás las dos aplicaciones principales son:

  • cuándo se utilizará la macro en el interior csname...endcsname
  • al tener el valor actual en el momento de un edef, marca, etc., es necesario.
¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *