El objetivo es que el usuario ingrese (a modo de argumento) una función lineal en una variable, por ejemplo 2*x+3
junto 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.
Mi intento original
\findvalueB
falló y no pareció gustarme el guión bajo, así que probé el método\findvalueA
que funcionó. ¿Por qué uno de estos funciona y el otro no?Al decidir cambiar los nombres de las variables para probar, me encontré con problemas obvios (por ejemplo, usar
y
pausas)\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 \defx
de 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 \write
o \detokenize
). Esto sucede "antes" de volver a escanear, donde \defx
habrá un único token. El resultado es que en el método C x
se 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 \def
y 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, sin
la expresión y la letra n
como posibles variables: tal caso podría manejarse, ¡pero necesitaría pensar más!)