特定宏中 \expandafter 的用途

特定宏中 \expandafter 的用途

考虑以下一组定义...

\documentclass[12pt]{article}

\def\foreach{%
  \begingroup
  \catcode`\^^M=12 \xforeach}

{\catcode`\^^M=12
  \gdef\xforeach #1^^M{%
    \first #1,,\endgroup}}

\def\first #1,{%
  \if,#1,
  \else Do something with `#1'.\par\expandafter\first
  \fi}

\begin{document}
\foreach a,cde
\end{document}

如果您能向我解释上面定义的\expandafter宏的含义(或功能),我会很高兴。\first

我的猜测是\expandafter关闭了\if-test,因此\first被插入了。如果我错了,我也不会感到惊讶。

我将感谢有经验的 TeX 用户的详细描述。

答案1

您的猜测是正确的。\expandafter总是跳过一个标记(在本例中为\first)并扩展下一个标记(在本例中为\fi)。扩展\fi(或\else,有点相似)结束当前条件并删除标记\fi

假设\expandafter不存在:的第一次迭代\first(我认为可以使用更好的名称)将是:

\if,a,
\else Do something with `a'.\par\first
\fi cde,,\endgroup

测试\if结果为假,因此\else将采用该分支。Do something with `a'.\par将被排版,然后\first再次展开。这一次,\first将抓取(所有内容到下一个 ,\fi cde作为参数,下一次迭代将是:

%   V----V frozen \relax
\if,\relax\fi cde,
\else Do something with `\fi cde'.\par\first
\fi cde,,\endgroup

现在测试将在条件完成之前\if看到标记,因此 TeX 会插入一个\fi冻结\relax并且测试\if,\relax结果为 false,因此 TeX 会跳到下一个\fi,也就是 之后的那个\relax。 Nowcde,会被(错误地)排版,然后 TeX 仍然在\else的第一次迭代的分支中\first,会看到另一个\else并会抱怨:

! Extra \else.
\first #1,->\if ,#1, \else
                           Do something with `#1'.\par \first \fi
l.19 \foreach a,cde

也就是说,我会稍微改变一下你的代码:

  1. 我会在循环开始之前发出\endgroup,这样你就不必担心,例如,PGF 内部定义的常见问题\foreach
  2. 我会把所有的Do something with `#1' 外部条件,以便参数中的可能条件标记(,,,\if等等)不会干扰循环的条件;并且\else\fi
  3. 我会使用更安全的排空测试(见这里举几个例子);\if,#1,如果参数中有条件标记(如上所示),并且循环的项目包含逗号(例如),则很危险\foreach a,{,b},c。更好的是,我会使用一个唯一的标记来测试循环的结束,这样就可以允许空项(它们不在您当前的代码中)。

也就是说,以下是更改后的代码:

\documentclass{article}
\makeatletter
\def\foreach{%
  \begingroup
  \catcode`\^^M=12 \xforeach}
{\catcode`\^^M=12
  \gdef\xforeach #1^^M{%
  \endgroup%
    \first #1,,}}
\def\first #1,{%
  \if\relax\detokenize{#1}\relax
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
    {\@gobble}%
    {Do something with `#1'.\par}%
  \first}
\makeatother
\begin{document}
\foreach a,cde
\end{document}

您还可以扩展它以将do something代码内联:

\documentclass{article}
\makeatletter
\def\foreach{%
  \begingroup
  \catcode`\^^M=12 \xforeach}
{\catcode`\^^M=12
  \long\gdef\xforeach #1^^M#2{%
  \endgroup%
  \def\marian@temp##1{#2}%
    \first #1,,}}
\def\first #1,{%
  \if\relax\detokenize{#1}\relax
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
    {\@gobble}%
    {\marian@temp{#1}}%
  \first}
\makeatother
\begin{document}
\foreach a,cde
  {Do something with `#1'.\par}
\end{document}

或者expl3使用\clist_map_inline:nn

\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\cs_new_protected:Npn \foreach
  {
    \group_begin:
      \char_set_catcode_other:N \^^M
      \__marian_foreach:wn
  }
\group_begin:
\char_set_catcode_other:N \^^M
\cs_new_protected:Npn \__marian_foreach:wn #1 ^^M #2
  {
    \group_end:
    \clist_map_inline:nn {#1} {#2}
  }
\group_end:
\ExplSyntaxOff
\begin{document}
\foreach a,cde
  {Do something with `#1'.\par}
\end{document}

相关内容