我有一个用例,我需要在文档中重复进行一些数学计算(例如,在 TikZ 循环中计算许多不同圆的半径)。即使在使用 LaTeX 编写文档多年后,我仍然无法找出将宏作为参数传递给其他宏的最佳或正确方法。下面我提供了一个非常简单的例子来说明我所谈论的事情。
我非常确定这与宏扩展有关,宏与函数的不同之处,以及 TeX 如何在控制代码 \MyAdd 后插入空格。我找到了一些关于内部情况的详细解释。但我找不到解决方案。什么是最简单但最正确的方法来获取一个宏的输出,可能将其存储在“变量”中,并将整个宏或变量传递给另一个宏进行进一步处理?
谢谢。
\documentclass{article}
\usepackage{xparse}
\usepackage{xfp}
\NewDocumentCommand{\MyAdd}{m m}{
\fpeval{#1 + #2}
}
\NewDocumentCommand{\MyMult}{m m}{
\fpeval{#1 * #2}
}
\NewDocumentCommand{\MyArgs}{m m}{
%% Inspect arguments as passed
\immediate\write20{Arg 1: #1}
\immediate\write20{Arg 2: #2}
}
\setlength{\parindent}{0pt}
\begin{document}
Correct: \MyAdd{1}{3}\\
Correct: \MyMult{4}{7}\\
%% Store results as macros themselves
\def\factA{\MyAdd{1}{3}}
\def\factB{\MyAdd{4}{7}}
Correct: \factA \\
% The next two lines generate the following errors:
% - Erroneous variable \fpeval used!
% - Missing * inserted.
% - Invalid operation fp_to_decimal(nan)
% \MyMult{\factA}{\factB}\\ %error
% \MyMult{\MyAdd{1}{3}}{\MyAdd{4}{7}}\\ %error
\MyArgs{\factA}{\factB}
%% Writes the following to the log. Note the space after \MyAdd
% Arg 1: \MyAdd {1}{3}
% Arg 2: \MyAdd {4}{7}
\end{document}
答案1
\MyAdd
输出中的空格\MyArgs
是完全标准的。任何潜入\write
操作的控制字后面都会有一个空格。
为什么?假设你有\write20{\relax and \relax}
。根据规则,有不\relax
形成标记时,第一个标记后面会有一个空格。如果 TeX 没有在控制字后添加空格,则输出将变成
\relaxand \relax
这是个问题,不是吗?
您应该担心代码中添加的其他几个空格。您的第一个定义完全等同于
\NewDocumentCommand{\MyAdd}{m m}{ \fpeval{#1 + #2} }
因为换行符会被转换为空格。 中的参数说明符之间的空格\NewDocumentCommand
会被忽略。 替换文本中的空格不会被忽略。 但是,如果您按照 中的方式编写代码,但在 之后\ExplSyntaxOn
,换行符和空格将被忽略。
不过,您的代码的主要问题是另一个问题。
您应该使用\NewExpandableDocumentCommand
和\MyAdd
命令\MyMult
。\edef
如果您想将结果存储在控制序列中。
\documentclass{article}
\usepackage{xparse}
\usepackage{xfp}
\NewExpandableDocumentCommand{\MyAdd}{m m}{%
\fpeval{#1 + #2}%
}
\NewExpandableDocumentCommand{\MyMult}{m m}{%
\fpeval{#1 * #2}%
}
\NewDocumentCommand{\MyArgs}{m m}{%
%% Inspect arguments as passed
\immediate\write20{Arg 1: #1}%
\immediate\write20{Arg 2: #2}%
}
\setlength{\parindent}{0pt}
\begin{document}
Correct: \MyAdd{1}{3}\\
Correct: \MyMult{4}{7}
%% Store results as macros themselves
\edef\factA{\MyAdd{1}{3}}
\edef\factB{\MyAdd{4}{7}}
Correct: \factA
% The next two lines generate the following errors:
% - Erroneous variable \fpeval used!
% - Missing * inserted.
% - Invalid operation fp_to_decimal(nan)
\MyMult{\factA}{\factB}
\MyMult{\MyAdd{1}{3}}{\MyAdd{4}{7}}
\MyArgs{\factA}{\factB}
%% Writes the following to the log. Note the space after \MyAdd
% Arg 1: \MyAdd {1}{3}
% Arg 2: \MyAdd {4}{7}
\end{document}
如果您用 定义\MyAdd
和,它们将是\MyMult
,所以它们不会在内部扩展,所以这个扩展定义根本不会执行任何操作。\NewDocumentCommand
\protected
\edef
答案2
正如您自己指出的那样,有一些虚假空格。我通过添加 删除了它们%
。
\documentclass{article}
\usepackage{xparse}
\usepackage{xfp}
\NewDocumentCommand{\MyAdd}{m m}{%
\fpeval{#1 + #2}%
}
\NewDocumentCommand{\MyMult}{m m}{%
\fpeval{#1 * #2}%
}
\NewDocumentCommand{\MyArgs}{m m}{%
%% Inspect arguments as passed
\typeout{Arg 1: #1}%
\typeout{Arg 2: #2}%
}
\setlength{\parindent}{0pt}
\begin{document}
Correct: \MyAdd{1}{3}\par
Correct: \MyMult{4}{7}\par
%% Store results as macros themselves
\def\factA{\MyAdd{1}{3}}
\def\factB{\MyAdd{4}{7}}
Correct: \factA \par
Correct: \factB \par
% The next two lines generate the following errors:
% - Erroneous variable \fpeval used!
% - Missing * inserted.
% - Invalid operation fp_to_decimal(nan)
\MyMult{\factA}{\factB}\par %error
\MyMult{\MyAdd{1}{3}}{\MyAdd{4}{7}}\par %error
\MyArgs{\factA}{\factB}
%% Writes the following to the log. Note the space after \MyAdd
% Arg 1: \MyAdd {1}{3}
% Arg 2: \MyAdd {4}{7}
\end{document}
但请注意,
\def\factA{\MyAdd{1}{3}}
\def\factB{\MyAdd{4}{7}}
你可能想要
\edef\factA{\MyAdd{1}{3}}
\edef\factB{\MyAdd{4}{7}}
因为如果继续使用\def
s,进一步嵌套将会引起麻烦。