目标是让用户输入(为了论证)一个变量的线性函数,例如,2*x+3
连同变量(这并不重要)和变量的值。然后我想计算函数的值。下面的方法有效,但我现在认为它更多的是偶然起作用的。
我最初的尝试
\findvalueB
失败了,似乎不喜欢下划线,所以我尝试了\findvalueA
其中一种有效的方法。为什么其中一个有效,而另一个无效?当决定切换变量名进行测试时,我遇到了明显的问题(例如使用
y
breaks\mytoks
。因此我尝试对变量名进行硬编码,但再次失败。基本思想似乎可行,如下图所示。为什么这次尝试会失败?
大概我没有理解的地方有一个共同点,这可能是对某些事情的根本误解,我想纠正一下。我对解释比代码解决方案更感兴趣,尽管两者都很好。
\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}
答案1
方法 B 的失败很容易解释。重新扫描材料会用当前适用的类别代码读取它。当你使用 \findvalueB
,您在文档中,因此_
类别为“数学下标”,而不是“字母”(:
稍后您也会发现这是错误的)。人们经常对此感到惊讶,因为他们认为重新扫描将使用定义命令时设置的代码。这是使用重新扫描相当棘手的一件事。
方法 C 的失败更难发现。这里发生的情况是,TeX\defx
在重新扫描时“有帮助地”在后面插入了一个空格,就像它在改变标记化时在任何其他控制序列后插入空格一样(例如在\write
或中\detokenize
)。这发生在重新扫描“之前”,其中\defx
将是一个标记。结果是在方法 C 中x
定义为后面跟着一个必需的空格:没有空格,所以我们得到错误。这在方法 A 中不会发生,因为这里的空格插入在后面\def
和前面#1
(它们是重新扫描之前的单独标记)。因此这种方法可以工作。
这两个问题都表明了为什么许多 TeX 程序员不喜欢重新扫描:我倾向于避免它。我们在 LaTeX3 中提供了合理的包装器,但您的问题表明它仍然很难做到正确。您真正想要的是将所有变量字母替换为值。因此,我根本不会重新扫描,而是使用
\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 }
}
它不存在任何这些问题。(我假设你没有例如sin
在表达式中和字母n
作为可能的变量:这种情况可以处理,但需要更多的思考!)