Funciones simbólicas: cambiar catcodes y \tl_rescan:nn

Funciones simbólicas: cambiar catcodes y \tl_rescan:nn

El objetivo es que el usuario ingrese (a modo de argumento) una función lineal en una variable, por ejemplo 2*x+3junto con la variable (esto no es crucial) y un valor para la variable. Entonces me gustaría calcular el valor de la función. Lo siguiente funciona, pero ahora estoy pensando que funciona más por accidente que cualquier otra cosa.

  1. Mi intento original \findvalueBfalló y no pareció gustarme el guión bajo, así que probé el método \findvalueAque funcionó. ¿Por qué uno de estos funciona y el otro no?

  2. Al decidir cambiar los nombres de las variables para probar, me encontré con problemas obvios (por ejemplo, usar ypausas) \mytoks. Así que intenté codificar un nombre de variable, pero nuevamente fallé. La idea básica parece funcionar como se muestra en la parte inferior. ¿Por qué falla este intento?

Presumiblemente hay un hilo conductor en lo que no entiendo y probablemente sea un malentendido fundamental de algo que me gustaría corregir. Estoy más interesado en la explicación que en una solución de código, aunque ambas estarían bien.

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\newtoks\mytoks

\NewDocumentCommand {\findvalueA} { m m m }
    {
        \group_begin:
        \char_set_catcode_active:N #1
        \tl_rescan:nn{}{\def#1{#2}}
        \tl_rescan:nn{}{\mytoks={#3}}
        \fp_set:Nn \l_tmpa_fp {\the\mytoks}
        \fp_use:N \l_tmpa_fp
        \group_end:
    }

\NewDocumentCommand {\findvalueB} { m m m }
    {
        \group_begin:
        \char_set_catcode_active:N #1
        \tl_rescan:nn{}{\def#1{#2}}
        % underscores=bad
        \tl_rescan:nn{}{\tl_set:Nn \l_tmpa_tl {#3}}
        % or
        % \tl_rescan:nn{}{\fp_eval:n {#3}}
        \fp_use:N \l_tmpa_fp
        \group_end:
    }

    \NewDocumentCommand {\findvalueC} { m m }
    {
        \group_begin:
        \char_set_catcode_active:N x
        \tl_rescan:nn {} {\defx{#1}}
        \tl_rescan:nn {} {\mytoks={#2}}
        \fp_set:Nn \l_tmpa_fp {\the\mytoks}
        \fp_use:N \l_tmpa_fp
        \group_end:
    }

\ExplSyntaxOff
\begin{document}

% works
\findvalueA{x}{5}{2*x+3}

% fails
%\findvalueB{x}{5}{2*x+3}

% fails
%\findvalueC{5}{x+2}

The method of findvalueC works down here but not above: if $x=5$, then $x+2=$
\ExplSyntaxOn
\group_begin:
\char_set_catcode_active:N x
\defx{5}
\fp_eval:n {x+2}
\group_end:
\ExplSyntaxOff

\end{document}

Respuesta1

El fracaso del método B es bastante fácil de explicar. El material que se vuelve a escanear lo lee con los códigos de categoría actualmente aplicables. Cuando ustedusar \findvalueB, usted está en el documento y por eso _tiene la categoría 'subíndice matemático', no 'letra' (también descubrirá que eso :es incorrecto, un poco más adelante). La gente suele sorprenderse con esto, ya que imaginan que la nueva exploración utilizará los códigos establecidos cuando se definió el comando. Esto es algo que hace que el uso de la reexploración sea bastante complicado.

El fracaso del método C es más difícil de detectar. Lo que sucede aquí es que TeX inserta 'útilmente' un espacio después \defxde volver a escanear, de la misma manera que lo hace después de cualquier otra secuencia de control al alterar la tokenización (por ejemplo, en \writeo \detokenize). Esto sucede "antes" de volver a escanear, donde \defxhabrá un único token. El resultado es que en el método C xse define como seguido de un espacio requerido: no hay espacio, por lo que obtenemos el error. Eso no sucede en el método A ya que el espacio aquí se inserta después \defy antes #1(son tokens separados antes de volver a escanear). Por tanto, este método funcionará.

Ambos problemas muestran por qué muchos programadores de TeX no prefieren el reescaneo: yo tiendo a evitarlo. Hemos proporcionado envoltorios razonables en LaTeX3, pero sus problemas muestran que todavía es complicado hacerlo bien. Lo que realmente quieres es reemplazar todas las letras de las variables con el valor. Por lo tanto, no volvería a escanear en absoluto, pero usaría

\NewDocumentCommand { \findvalueD } { m m m }
  {
    \tl_set:Nn \l_tmpa_tl {#3}
    \tl_replace_all:Nnn \l_tmpa_tl {#1} {#2}
    \fp_eval:n { \l_tmpa_tl }
  }

que no tiene ninguno de estos problemas. (Supongo que no tiene, por ejemplo, sinla expresión y la letra ncomo posibles variables: tal caso podría manejarse, ¡pero necesitaría pensar más!)

información relacionada