如何将宏作为参数传递给其他宏

如何将宏作为参数传递给其他宏

我有一个用例,我需要在文档中重复进行一些数学计算(例如,在 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}}

因为如果继续使用\defs,进一步嵌套将会引起麻烦。

相关内容