为什么 \renewcommand 的参数需要在 \foreach 中重复 #

为什么 \renewcommand 的参数需要在 \foreach 中重复 #

介绍:

当您\renewcommand在另一个宏或环境中使用时,需要将 加倍才能##访问内部宏的参数。这可以作为区分内部宏##1和外部宏的一种方式#1(即使外部宏没有任何参数,它也提供错误检查)。例如:

\newcommand*{\OuterCommand}{%
    \renewcommand*{\SomeCommand}[1]{\color{red}##1}%
    ....
}%  

下面的参考资料提供了有关此内容的更多详细信息。

问题:

#看起来,在循环内需要进行相同的加倍,\foreach如下面的 MWE 所示。

我花了很长时间才弄清楚,因为下面的 MWE 给出的错误消息是:

\pgffor@body 定义中的参数编号非法

这是一个非常深的循环嵌套\foreach,所以我确信问题出在我嵌套得\foreach太深了。令人惊讶的是,如果你跳过错误,输出仍然是正确的,所以这让定位问题变得更加困难。

问题:

  • 既然\foreach没有像 这样的参数#1(就像\newcommand有 一样),为什么需要#在 内的定义中将加倍\foreach
  • 如果您只是忽略错误并按回车键继续处理,那么输出怎么仍然正确呢?

参考:

代码:

\documentclass{article}
\usepackage{xcolor}
\usepackage{pgffor}

\newcommand*{\MyList}{A,B,C}%
\newcommand{\SomeCommand}[1]{#1}%

\begin{document}
\foreach \x in \MyList {%
    \renewcommand*{\SomeCommand}[1]{\color{red}#1}%  Using ## here eliminates the error.
    \par\SomeCommand{\x}%
}%
\end{document}

答案1

在内部,foreach 的定义将把循环主体保存在宏中,因此就像(如果循环a,b,...

\def\body{%
\renewcommand*{\SomeCommand}[1]{\color{red}#1}%  Using ## here eliminates the error.
    \par\SomeCommand{\x}%
}%
}
\def\x{a}\body
\def\x{b}\body
...

初始\def(或者\newcommand如果您愿意)将要求#在正文中输入。如上所示,您会在不接受参数的情况##下收到错误,您必须输入 ,正如您所发现的。#1\body##1

可以以不需要这样做的方式定义循环(例如,它可以使用令牌寄存器而不是宏来保存主体)但这显然不是这里的情况。


请注意,需要加倍#与存在内部宏定义无关。这并不是说内部宏需要##1引用第一个参数,而只是内部宏定义需要两个标记#1来引用第一个参数,并将 放入#宏主体中,您需要##。要了解这一点,请考虑没有 的定义##1

你可以走了

\let\hash=#

并将\hash被允许,#但你不能将宏定义为

\def\definehash{\let\hash=#}

因为会产生

! Illegal parameter number in definition of \definehash.

您需要在宏定义中##引用文字#。因此需要

\def\definehash{\let\hash=##}

您在评论中询问为什么双重嵌套定义需要四个#。同样,内部定义也没有什么特别之处,它们##1在评估时永远不会“看到”,它们只会看到一个#。如果您尝试定义

\def\ddefinehash{\def\definehash{\let\hash=##}}

定义将会顺利进行,但是如果你尝试执行,\ddefinehash你会发现它只有一个单身的 #在其定义中,因此您会像以前一样收到错误:

! Illegal parameter number in definition of \definehash.
<to be read again> 
                   }
l.12 \ddefinehash

所以你需要 #进入定义\definehash,并且每个都必须输入,所以##你最终会得到

\def\ddefinehash{\def\definehash{\let\hash=####}}

综合起来:

{
\let\hash=#
}

{
\def\definehash{\let\hash=##}
}

{
\def\ddefinehash{\def\definehash{\let\hash=####}}
\ddefinehash
\definehash

\show\ddefinehash
\show\definehash
\show\hash
}

\bye

> \ddefinehash=macro:
->\def \definehash {\let \hash =####}.
l.15 \show\ddefinehash

? 
> \definehash=macro:
->\let \hash =##.
l.16 \show\definehash

? 
> \hash=macro parameter character #.
l.17 \show\hash

? 
 )
No pages of output.

相关内容