为什么迭代标记的参数应该以非空宏结尾,以及该宏实际上应该包含什么?

为什么迭代标记的参数应该以非空宏结尾,以及该宏实际上应该包含什么?

我在这里问过这个问题不止一次,但我得到的回答都是“使用非空宏更安全”。我从未被告知原因,而且(更重要的是)从未明确说明宏应该包含什么。

此代码示例是一个“循环”宏,它迭代其参数的每个标记:

\def\zEnd{\zEnd}
\def\zzIterator#1{%
    \ifx#1\zEnd
    \else
      #1%
      \expandafter\zzIterator
    \fi
  }
\def\zIterator#1{\zzIterator#1\zEnd}
\zIterator{hello}

有两个问题:

  1. 为什么\def\zEnd{\zEnd}不应该为空(不应该是\def\zEnd{})?
  2. 花括号内的宏具体是什么真的很重要吗\zEnd,不管所括起来的宏是它本身\zEnd还是其他什么(例如\zIterator\zzIterator或诸如此类)?

答案1

使用“夸克”(定义为扩展为自身的宏)的原因是它永远无法匹配还要别的吗.如果您使用

\def\zEnd{}

那么你的标记\ifx等于\empty,所以像

\zIterator{hello\empty more tokens}

将提前停止。除了标记本身之外,其他任何定义都一样:您需要确保标记不在输入中。使用夸克,我们只有一个“受限”标记。

答案2

此代码示例是一个“循环”宏,它迭代其参数的每个标记:

\def\zEnd{\zEnd}
\def\zzIterator#1{%
    \ifx#1\zEnd
    \else
      #1%
      \expandafter\zzIterator
    \fi
  }
\def\zIterator#1{\zzIterator#1\zEnd}
\zIterator{hello}

有两个问题:

  1. 为什么\def\zEnd{\zEnd}不应该为空(不应该是\def\zEnd{})?
  2. 花括号内的宏具体是什么真的很重要吗\zEnd,不管所括起来的宏是它本身\zEnd还是其他什么(例如\zIterator\zzIterator或诸如此类)?

该令牌\zEnd用作结束迭代/递归的“标记令牌”。

广告 1:\ifx-comparison 通过比较标记的含义来触发循环的终止。实际上,除了 之外,不会有很多标记\zEnd被定义为扩展为。但可能有许多短宏在某个阶段(暂时)被定义为根本不传递任何标记。如果被定义为\zEnd,则此类宏标记会错误地终止循环。\zend\def\zEnd{}

广告 2:这无所谓。重要的是,选择/定义标记标记的方式应确保在参数/要迭代的标记集中不会出现具有相同含义的标记。

顺便说一句:“迭代其参数的每个标记”的说法是错误的:

底层循环宏\zzIterator不处理单个标记,但会处理非分隔参数。因此,循环宏不会遍历其参数的每个标记,而是按非分隔参数的方式遍历其参数,直到遇到具有两个含义相同的前导标记的非分隔参数,或遇到含义与 含义相同的标记\zEnd

“非分隔参数”意味着删除一级花括号(如果存在)并丢弃明确的空格标记。

例如,看看会发生什么:

\def\zEnd{\zEnd}
\def\zzIterator#1{%
    \ifx#1\zEnd
       Iteration ends.
    \else
      Iteration processes: #1 %
      \expandafter\zzIterator
    \fi
  }
\def\zIterator#1{\zzIterator#1\zEnd}
\def\gobble#1{}%
\let\foobar=\zEnd

\zIterator{hel\foobar lo\gobble}
\bigskip

\zIterator{h{ell}o}
\bigskip

\zIterator{ {h} {ell} {o} }
\bigskip

\zIterator{he{ll\gobble}o\gobble}
\bigskip

% Don't do:
%\zIterator{he{ll}o}
% Here \ifx will compare l to l and then \zEnd will be carried out, 
% yielding a never terminating recursion as \zEnd calls itself again
% and again...
\bye

反转要比较的标记\ifx会更安全一些,因为您不会陷入\zIterator{he{ll}o}陷阱:

\def\zEnd{\zEnd}
\def\zzIterator#1{%
    \ifx\zEnd#1%<---- reversed.
    \else
      #1%
      \expandafter\zzIterator
    \fi
}
\def\zIterator#1{\zzIterator#1\zEnd}
\zIterator{hello}

该循环仍然可以通过参数中等于\zEnd和(不平衡)\if../ \else/ 的标记来超越。\fi

检测参数是否为空更加安全一些:

\long\def\firstoftwo#1#2{#1}
\long\def\secondoftwo#1#2{#2}
%%=============================================================================
%% Check whether argument is empty:
%%=============================================================================
%% \CheckWhetherNull{<Argument which is to be checked>}%
%%                  {<Tokens to be delivered in case that argument
%%                    which is to be checked is empty>}%
%%                  {<Tokens to be delivered in case that argument
%%                    which is to be checked is not empty>}%
%%
%% Due to \romannumeral0-expansion the result is delivered after two
%% expansion-steps/after two "hits" by \expandafter.
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
%%
\long\def\CheckWhetherNull#1{%
  \romannumeral0\expandafter\secondoftwo\string{\expandafter
  \secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \secondoftwo\string}\expandafter\firstoftwo\expandafter{\expandafter
  \secondoftwo\string}\firstoftwo\expandafter{} \secondoftwo}%
  {\firstoftwo\expandafter{} \firstoftwo}%
}%
%%=============================================================================
\long\def\IIterator#1#2{%
  \expandafter\CheckWhetherNull\expandafter{\firstoftwo{}#1}{%
     Iteration terminated.%
  }{%
    Processing: #2 %
    \expandafter\IIterator\expandafter{\firstoftwo{}#1}%
  }%
}%
\long\def\Iterator#1{\IIterator{#1{}}#1{}}%
%%
\Iterator{hello}%
\bigskip\par
\Iterator{he{ll}o}%
\bigskip\par    
Of course this does---like the other variants---not take space-tokens
into account because \firstoftwo{\TeX}{} discards space-tokens that
precede non-delimited arguments:
\bigskip\par
\Iterator{ h e l l o }%

\bye

相关内容