¿Por qué el modismo \expandafter\@firstoftwo?

¿Por qué el modismo \expandafter\@firstoftwo?

Veo muchas macros que son pruebas condicionales seguidas de un {true}{false}par definido para que den como resultado:

\expandafter\@firstoftwo

o

\expandafter\@secondoftwo

¿Por qué están estas \expandafterahí? ¿Habría pensado que simplemente atraparían el primer aparato ortopédico del siguiente {true}{false}par?

Respuesta1

Los \expandafters son para tratar lo siguiente \elseo \fi. Como en la pregunta a la que se vincula Ryan, el código completo es algo como:

\def\ifeq#1#2{%
 \ifx#1#2\relax
   \expandafter\@firstoftwo
 \else
   \expandafter\@secondoftwo
 \fi
}

Rastreemos lo que sucede. Ponemos \ifeq\stuff\nonsense{true}{false}en nuestro documento. El \ifeqabsorbe el \stuffy \nonsenseasí tenemos después de la primera expansión:

\ifx\stuff\nonsense\relax
  \expandafter\@firstofone
\else
  \expandafter\@secondoftwo
\fi
{true}{false}

Supongamos que así \stuffes \nonsense(es decir, que el condicional es verdadero). Luego \ifxcomienza a expandir todo en su camino "verdadero", que se define como todo hasta el siguiente \elseo \fi(anidamiento de módulo). El punto clave es que comienza a expandirse.primeroy no mira hacia adelante para encontrar el \elseo \fi. TeX cree que lo sabrá cuando llegue el momento. Entonces tenemos:

  \expandafter\@firstoftwo
\else
  \expandafter\@secondoftwo
\fi
{true}{false}

TeX ahora amplía eso \expandafter. Esto tiene el efecto de extenderse hacia \@firstoftwoel \elsey expandirlo. "Expandir" \elsesignifica eliminarlo y todo lo que coincida \fide la transmisión. Entonces nos quedamos con:

\@firstoftwo{true}{false}

Y luego esto se expande a lo simple true.

Sin la \expandafters allí, llegamos a:

  \@firstoftwo
\else
  \@secondoftwo
\fi
{true}{false}

TeX todavía está expandiendo la verdadera rama, por lo que se expande \@firstoftwo. Esto absorbe dos fichas/grupos reforzados de la secuencia. Estos resultan ser \elsey \@secondoftwo. Luego deja el primero en la secuencia para que obtengamos

\else
\fi
{true}{false}

Coincide \elsecon el condicional, por lo que TeX absorbe esto y todo hasta \fidejarlo {true}{false}en la secuencia. Que no es lo que queríamos.

En resumen, \expandaftersdeben eliminar el procesamiento condicional antes de queresultadodel condicional se expande, asegurando así que el resultado del condicional vea los siguientes bits en la secuencia y no los bits que quedan del condicional inacabado.

Respuesta2

Yo atacaría esto desde un punto de vista diferente. Los condicionales primitivos en TeX prueban una condición, que puede absorber tokens del flujo de entrada o no hasta que se pueda establecer la verdad o falsedad de la condición. Entonces, denotemos por <IF>el condicional primitivo junto con la lista (posiblemente vacía) de tokens que deben ser absorbidos. Por ejemplo, \ifhmodeno necesita ficha, \ifxnecesita dos. En algunos casos ( \if, \ifcat, \ifnum, \ifdim) TeX realiza expansiones para encontrar el tipo de tokens requerido para la prueba; en otros ( \ifx, \ifmmode, \ifhmode, \ifvmode \ifinner, \iftrue, \iffalse) no se realiza ninguna expansión. Así <IF>se indicarán los tokens condicionales y requeridos.despuésSe ha producido una expansión y se puede probar la condición.

La construcción típica a la que te refieres es

<IF>
  \expandafter\@firstoftwo
\else
  \expandafter\@secondoftwo
\fi

donde ya tenemos

\long\def\@firstoftwo#1#2{#1}
\long\def\@secondoftwo#1#2{#2}

De manera más general, tenemos

<IF><true>\else<false>\fi

o

\IF<true>\fi

donde ambos <true>y <false>pueden estar vacíos.

La condición es verdadera.

TeX simplemente se eliminará <IF>del flujo de entrada, dejando

<true>\else<false>\fi

o

<true>\fi

La condición es falsa.

TeX busca el siguiente \elsetoken, teniendo en cuenta los condicionales anidados que pueden aparecer <true>sin expandir nada. Por lo tanto , se omitirá una \elsepertenencia a un \if...\else\fiinterior anidado. <true>La expansión también está vacía en este caso y todas las fichas hasta \elsedesaparecerán. En caso de que no \elsese encuentre ninguna coincidencia, TeX dejará de buscar la coincidencia \fique debería estar en alguna parte. Entonces, en los dos casos, obtendríamos

<false>\fi

o simplemente nada si no \elsehabía ninguna sucursal.

Esto se demuestra con la siguiente entrada TeX:

\def\showx{\show\x}
\def\showif{\afterassignment\showx
  \expandafter\def\expandafter\x\expandafter}

\showif{\ifvmode<true>\else<false>\fi}
\showif{\ifvmode<true>\fi}
\showif{\ifhmode<true>\else<false>\fi}
\showif{\ifhmode<true>\fi}
\bye

Al ejecutar TeX en él se producirá la siguiente transcripción:

This is TeX, Version 3.1415926 (TeX Live 2012)
(./plkfi.tex
> \x=macro:
-><true>\else <false>\fi .
\showx ->\show \x

l.5 \showif{\ifvmode<true>\else<false>\fi}

?
> \x=macro:
-><true>\fi .
\showx ->\show \x

l.6 \showif{\ifvmode<true>\fi}

?
> \x=macro:
-><false>\fi .
\showx ->\show \x

l.7 \showif{\ifhmode<true>\else<false>\fi}

?
> \x=macro:
->.
\showx ->\show \x

l.8 \showif{\ifhmode<true>\fi}

?

¿Qué pasa después?

  • La expansión de \elseconsiste en eliminar todo hasta la coincidencia \fiy no dejar nada en el flujo de entrada.Los condicionales anidados se tendrán en cuenta como antes.

  • La expansión de \fiestá vacía.

El rol de\expandafter

Ahora tenemos las bases sobre las cuales podemos caer \expandafter.

Veamos un uso típico:

\def\@ifundefined#1{%
  \expandafter\ifx\csname#1\endcsname\relax
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi}

Lo que queremos es poder decir

\@ifundefined{foo}{T}{F}

Entonces, la macro construida con el argumento como nombre se compara con \relax(esta es realmente la parte que no es interesante) y luego se sigue la rama verdadera o falsa.

Después de la eliminación del <IF>nos quedamos con

\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi{T}{F}

y ahora TeX amplía debidamente el primer token. Esto desencadena la expansión de \elsey aquí es donde comienza la diversión.

La expansión de \expandafterconsiste en expandir (si es posible) el token después del siguiente y desaparecer.Así \elsese expande según la regla anterior y nos queda

\@firstoftwo{T}{F}

eso resulta en salir Ten el flujo de entrada.

Supongamos ahora que la condición es falsa. Luego <IF>se retira junto con todo hasta \elsedejar

\expandafter\@secondoftwo\fi{T}{F}

Ahora \expandafterhace su trabajo de expandirse \fiy desaparecer. Así obtenemos

\@secondoftwo{T}{F}

que finalmente se va F.

Nota IMPORTANTE

En el caso de que \@ifundefined{foo}{T}{F}podamos llegar a To Fsin nuncaejecutandoun comando: solo se ha utilizado la expansión macro. Esto hace que \@ifundefineduna macro definida de manera similar sea utilizable dentro \edef:

\edef\test{\@ifundefined{foo}{T}{F}}

será equivalente a

\def\test{T}

en caso \fooestá definido (y no es equivalente a \relax, como es habitual en LaTeX) o a

\def\test{F}

si \foono está definido (o es equivalente a \relax).


¿Qué pasaría sin \expandafter? Con un verdadero condicional, TeX se enfrentaría a

\@firstoftwo\else\@secondoftwo\fi{T}{F}

y los dos argumentos \@firstoftwoserían \elsey \@secondoftwo, que no harían nada útil, ¿verdad?

De manera similar, para una condición falsa obtendríamos

\@secondoftwo\fi{T}{F}

y nuevamente las cosas saldrían mal.

Respuesta3

El contexto completo de dicho código se encuentra en macros condicionales como

\def\IfZero#1{%
  \ifnum0=#1\relax
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
}

Los \expandafters son necesarios si observa el código en una forma menos formateada:

\def\IfZero#1{\ifnum0=#1\relax\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi}

(disculpas por la larga línea) en el que no se da ninguna pista sobre la estructura de los bloques condicionales de TeX. Como puede ver, el token "después" \@firstoftwoes \elsey el siguiente \@secondoftwoes \fi, que se expanden respectivamente en \expandafter. El propósito de esta tontería es que TeX no lea el condicional completo cuando expande el \ifnum; simplemente escanea hacia adelante hasta el bloque verdadero o falso correcto y continúa desde allí. ¡Los \elseo \fise dejan en el flujo de entrada! Una vez que se expanden, TeX escanea hasta el final del condicional y lo siguiente en el flujo de entrada es lo siguiente \IfZero.

Sin ninguno de los dos \expandafter, los "dos" consumidos por \@firstoftwoy \@secondoftwoserían, respectivamente, \else\@secondoftwoy \fiel siguiente macro argumento, en lugar de lo que realmente se pretende, es decir, los dos siguientes macro argumentos que se toman como el "segundo" y el "tercer" "argumento". de \IfZero.

información relacionada