为什么用 \NewDocumentCommand 定义的这个命令的行为与表格列规范中的 \def 不同?

为什么用 \NewDocumentCommand 定义的这个命令的行为与表格列规范中的 \def 不同?

乍一看

\NewDocumentCommand{\foo}{m}{#1}

\def\foo#1{#1}

应该相同,并且在大多数情况下它们实际上是相同的,但是当参数没有明确跟随时\foo,通过定义的命令\NewDocumentCommandMissing {出错,而使用则\def不会

这个问题有点像是以下问题的后续问题:>{...} 列规范中的宏延迟扩展 ,所以我将举这个例子来说明问题。看看 MWE:

\documentclass{article}
\usepackage{array}

\def\test#1{
\newcommand{\temp}[1][default]{##1}
#1\temp
}

\begin{document}

\begin{tabular}{>{\test}l}
[one]1\\
1\\
1\\
1
\end{tabular}

\end{document}

这个工作得很好,但是一旦我\test像这样定义

\NewDocumentCommand{\test}{m}{
\newcommand{\temp}[1][default]{##1}
#1\temp
}

错误不断出现。

我自己发现,如果你在列规范\expandafter之前添加,它可以与定义一起使用\test\NewDocumentCommand

\begin{tabular}{>{\expandafter\test}l}
[one]1\\
1\\
1\\
1
\end{tabular}

但我想补充一点,

\documentclass{article}
\usepackage{array}

\NewDocumentCommand{\test}{mm}{
\newcommand{\temp}[1][default]{##1}
#2#1\temp
}

\begin{document}

\begin{tabular}{>{\test{foo}}l}
[one]1\\
1\\
1\\
1
\end{tabular}

\end{document}

在这里我无法用 来解决问题\expandafter


所以我的问题是,它如何使工作与不抛出一堆或任何其他批量解决方案\NewDocumentCommand时的工作相同。\def\expandafter

答案1

正如我之前提到的,这正式属于“未定义行为”,使用collcell包然后照常处理数据。但无论如何,这是一个答案。

你必须理解\halign原始函数的工作原理以及 3 种括号技巧(TeXbook 附录 D,或了解牙套使用技巧/大括号技巧展示:}、\egroup、\iffalse{\fi} 等。)来理解这个答案。


首先,制作一个 MWE:

\documentclass{article}
\begin{document}


\ExplSyntaxOn

\protected\def\test     {\group_align_safe_begin: \testb}
\protected\def\testb #1 {\group_align_safe_end: #1}
    
\halign{\test\ignorespaces# \cr
123 \cr}

\ExplSyntaxOff

\end{document}

这里发生了什么?

根据定义,宏\test将执行以下操作......

  • 扩张\group_align_safe_begin:
  • 抢夺\ignorespaces令牌
  • 扩张\group_align_safe_end:

问题出现的原因是,当它这样做的时候......

  • 展开\group_align_safe_begin:→ 将主计数器增加 1
  • 抓取\ignorespacestoken → 运行到模板 u 部分的末尾。TeX 在对齐条目的末尾标记目标主计数器应为 1
  • 展开\group_align_safe_end:→返回主计数器。太迟了。

&看到时,它会理所当然地抱怨当前主计数器值为 0,而它应该是 1 才能正确结束对齐条目。

这里的问题是主计数器的明确调整,这在大多数情况下显然是需要的。

(附注:在这个特定的 MWE 中,你可以避免#

\halign{\test\ignorespaces\empty# \cr
123 \cr}

但如果你想窥视条目本身则不适用)

(顺便说一句,\expandafter你之前放的\test实际上没有任何效果,因为它\ignorespaces是不可扩展的;尽管如此,它还是让 TeX“看到”了\ignorespaces标记,并且“接触”了该#部分,从而将目标主计数器值设置为 0 而不是 1)


不过,你可以通过“撤消”效果来选择“退出”它(强烈不推荐):

\protected\def\test{\group_align_safe_end: \testa}  % remember to protect this, because the start of an alignment entry is initially expanded to look for e.g. \omit
\NewDocumentCommand\testa{m}{\group_align_safe_begin: #1}

它在上面的例子中确实起作用\halign


不用说,你可以找到禁用对齐保护是有害的例子。

对于诸如保护之类的事情\peek_analysis_map_inline:n显然是有用的(https://github.com/latex3/latex3/issues/1090),
对于诸如此类的事情clist_map_,它有点用处,
但对于参数抓取,我不确定。(也许抓取&可选参数内部的数据也算数。)

还有这个(虽然有点做作......无论如何谁需要以 \cr 作为输入......?)

\documentclass{article}
\begin{document}


\ExplSyntaxOn

% the following 4 lines are equivalent to the simple \protected\def  below

%\protected\def\test{\group_align_safe_end: \testa}  % remember to protect this, because the start of an alignment entry is initially expanded to look for e.g. \omit
%\NewDocumentCommand\testa{+m}{\group_align_safe_begin:
%   #1
%}

\protected\def\test #1{#1}

\halign{# \cr
    \test \cr}

\ExplSyntaxOff

\end{document}

相关内容