我正在开发一个解析器来计算向量的大小,似乎 sqrt() 函数在对包含对负数进行运算的 ^ 或 ** 运算符的表达式进行操作时会引发错误。这是我的 MWE:
\documentclass{article}
\usepackage{expl3}
\usepackage{xparse}
\ExplSyntaxOn
\cs_new:Npn \l_my_parsevector #1 #2 {%
\clist_set:Nn \l_tmpa_clist { #2 } % copy the vector to a clist
\clist_pop:NN \l_tmpa_clist {\x} % get the leftmost item
%\fp_zero_new:c { l__#1_fp } % new fp variable
\fp_set:cn { l__#1_fp } {\x} % assign it the first component
%[\x]
\clist_pop:NN \l_tmpa_clist {\y} % get the leftmost item
%\fp_zero_new:c { l__#1_fp } % new fp variable
\fp_set:cn { l__#1_fp } {\y} % assign it the second component
%[\y]
\clist_pop:NN \l_tmpa_clist {\z} % get the leftmost item
%\fp_zero_new:c { l__#1_fp } % new fp variable
\fp_set:cn { l__#1_fp } {\z} % assign it the third component
%[\z]
}%
\NewDocumentCommand{\VectorMagnitude}{ O{a} m }{%
\l_my_parsevector{#1}{#2}
%\fp_eval:n { sqrt(\x*\x+\y*\y+\z*\z) } % always gives correct result
\fp_eval:n { sqrt(\x^2+\y^2+\z^2) } % Invalid operation sqrt(-16)
}%
\ExplSyntaxOff
\begin{document}
\VectorMagnitude{0,-4,0}
\end{document}
当前文档的第 213 页interface3
似乎表明这是预期的行为,但表述得令人困惑。这种语法不起作用的原因是什么?我注意到,在两种情况下,将 -4 更改为 4 都会产生正确的结果。
答案1
您应该这样做(\x)^2
以保护可能的减号。如果没有括号,您将得到-4^2
正确评估为的结果-16
。
另一方面,你可以做得更好:
\documentclass{article}
\usepackage{expl3}
\usepackage{xparse}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\VectorMagnitude}{O{15}m}
{
\fp_eval:n
{
round ( sqrt( 0 \clist_map_function:nN { #2 } \latexerexetal_square:n ) , #1 )
}
}
\cs_new:Nn \latexerexetal_square:n { + (#1)^2 }
\ExplSyntaxOff
\begin{document}
\VectorMagnitude{0,-4,0}
\VectorMagnitude{1,1,1}
\VectorMagnitude{3,4,0}
\VectorMagnitude[4]{3,4,0,-1,3,pi}
\end{document}
该命令是完全可扩展的;例如
\edef\test{\VectorMagnitude[4]{3,4,0,-1,3,pi}}
将导致\test
具有6.6985
替换文本。
关于代码的一些注释。函数名不应以 开头l_
,因为 是变量的保留名称。它还应该具有参数的签名,因此您的代码\l_my_parsevector
实际上应该是
\my_parsevector:nn
并且应该用 来定义\cs_new_protected:Npn
,因为它进行赋值。
还应\clist_pop_left:NN
跟随着一个 clist 变量和一个 tl 变量,两者都没有括号;\x
不能用作tl
变量的名称。
如果你想提取 clist 变量中的第三个项目并将其设置为 fp 变量,那么更简单的说法是
\fp_set:Nn \l_tmpa_fp { \clist_item:Nn \l_tmpa_clist { 3 } }
而不是一个接一个地弹出项目。但我建议的方法要简单得多。
我们\clist_map_function:nN { #1 } \latexerexetal_square:n
一举夺得
\latexerexetal_square:n { 0 } \latexerexetal_square:n { -4 } \latexerexetal_square:n { 0 }
在下一阶段的扩展中,这将变成
+ (0)^2 + (-4)^2 + (0)^2
首字母0
仍然位于所有内容的前面(实际上,它并不是真正必要的);因此表达式的评估是正确的。
由于使用的所有东西都是完全可扩展的,我可以\VectorMagnitude
用 来定义\NewExpandableDocumentCommand
。可选参数是舍入的位数。