O objetivo é fazer com que o usuário insira (para fins de argumentação) uma função linear em uma variável, por exemplo, 2*x+3
junto com a variável (isso não é crucial) e um valor para a variável. Gostaria então de calcular o valor da função. O seguinte funciona, mas agora estou pensando que funciona mais por acidente do que qualquer outra coisa.
Minha tentativa original
\findvalueB
falhou e não pareceu gostar do sublinhado, então tentei o método\findvalueA
que funcionou. Por que um deles funciona e o outro não?Ao decidir mudar os nomes das variáveis para teste, me deparei com problemas óbvios (por exemplo, usando
y
breaks\mytoks
. Então tentei codificar um nome de variável, novamente falha. A ideia básica parece funcionar como mostrado na parte inferior. Por que essa tentativa falha?
Presumivelmente, há um ponto comum no que não estou entendendo e é provavelmente um mal-entendido fundamental de algo que gostaria de corrigir. Estou mais interessado na explicação do que em uma solução de código, embora ambos sejam bons.
\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}
Responder1
O fracasso do método B é muito fácil de explicar. A nova digitalização do material o lê com os códigos de categoria atualmente aplicáveis. Quando vocêusar \findvalueB
, você está no documento e também _
tem a categoria 'subscrito matemático', não 'letra' (você também descobrirá que isso :
está errado, um pouco mais tarde). As pessoas muitas vezes ficam surpresas com isso, pois imaginam que a nova varredura usará os códigos definidos quando o comando foi definido. Isso é algo que torna o uso da nova digitalização bastante complicado.
A falha do método C é mais difícil de detectar. O que acontece aqui é que o TeX insere 'de forma útil' um espaço depois \defx
de fazer a nova varredura, da mesma forma que faz depois de qualquer outra sequência de controle ao alterar a tokenização (por exemplo, em \write
ou \detokenize
). Isso acontece 'antes' da nova varredura, onde \defx
estará um único token. O resultado é que no método C x
é definido como sendo seguido por um espaço obrigatório: não há espaço, então obtemos o erro. Isso não acontece no método A, pois o espaço aqui é inserido antes \def
e depois #1
(são tokens separados antes da nova varredura). Portanto, este método funcionará.
Ambas as questões mostram por que a nova varredura não é preferida por muitos programadores de TeX: eu tendo a evitá-la. Fornecemos wrappers razoáveis no LaTeX3, mas seus problemas mostram que ainda é difícil acertar. O que você realmente deseja é substituir todas as letras das variáveis pelo valor. Portanto, eu não verificaria novamente, mas usaria
\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 não tem nenhum desses problemas. (Presumo que você não tenha, por exemplo, sin
a expressão e a letra n
como uma possível variável: tal caso poderia ser tratado, mas precisaria de mais reflexão!)