递归项目化宏输出和 \@ifnextchar 怪异之处

递归项目化宏输出和 \@ifnextchar 怪异之处

我遇到了我定义的宏的一些相当不寻常的行为。这个宏的具体目标最终是设置一些易于调用的东西,以生成我喜欢的分项列表,而不会产生缩进地狱。我省略了分项代码,因为它不会影响此行为。MWE 代码(附有我的注释)如下。

\documentclass{article}

\makeatletter
  \newcommand{\startfoo}{             % Macro Entry
    \@ifnextchar\foo                  %
      { START FOO \\ \@nextfoo }      % nextchar == \foo, start FOO section
      { } }                           % nextchar != \foo
  \newcommand\@nextfoo[2]{ #2 \\      % Display a foo. #1 is \foo
    \@ifnextchar\bar                  %
      { START BAR \\ \@nextbar }      % nextchar == \bar, start BAR section
      { \@checkfoo } }                % nextchar != \bar
  \newcommand\@nextbar[2]{ #2 \\      % Display a bar. #1 is \bar
    \@ifnextchar\zot                  %
      { START ZOT \\ \@nextzot }      % nextchar == \zot, start ZOT section
      { \@checkbar } }                % nextchar != \zot
  \newcommand\@nextzot[2]{ #2 \\      % Display a zot, #1 is \zot
    \@checkzot }                      %
  \newcommand\@checkzot{              % Check if nextchar is \zot
    \@ifnextchar\zot                  %
      { \@nextzot }                   % nextchar == \zot, continue section
      { \@endzot } }                  % nextchar != \zot
  \newcommand\@checkbar{              % Check if nextchar is \bar
    \@ifnextchar\bar                  %
      { \@nextbar }                   % nextchar == \bar, continue section
      { \@endbar } }                  % nextchar != \bar
  \newcommand\@checkfoo{              % Check if nextchar is \foo
    \@ifnextchar\foo                  %
      { \@nextfoo }                   % nextchar == \foo, continue section
      { \@endfoo } }                  % nextchar != \foo
  \newcommand\@endzot{ END ZOT \\     % End ZOT section
    \@checkbar}                       % Go check on BAR
  \newcommand\@endbar{ END BAR \\     % End BAR section
    \@checkfoo}                       % Go check on FOO
  \newcommand\@endfoo{ END FOO \\  }  % End FOO section, We're done
\makeatother

\begin{document}  % Expected Output:  | Actual Output:
  \startfoo       %
  \foo{foo1}      % START FOO         | START FOO
                  % foo1              | foo1
    \bar{bar1}    % START BAR         | START BAR
                  % bar1              | bar1
      \zot{zot1}  % START ZOT         | START ZOT
                  % zot1              | zota
      \zot{zota}  % zota              | zota
                  % END ZOT
                  % END BAR
  \foo{foo2}      % foo2              | foo2
                  % END FOO           | END ZOT
                  %                   | END BAR
                  %                   | END FOO
  THUD            % THUD              | THUD
\end{document}    %

对我来说,“似乎”发生的事情是\@ifnextchar比较\zort第二个 \foo 并计算为真,从而将该\foo行解释为\zort

\@nextzort并且\@nextbar功能上看起来完全相同,如果我注释掉所有包含 zort 的行,只剩下 foo 和 bar,那么宏似乎可以完美运行。

我显然遗漏了一些东西,但我需要帮助找出那是什么。我是不是走错了路?

答案1

编码有点奇怪,但主要错误是这\@ifnextchar是一个\ifx测试,因此所有未定义的命令测试结果相同。如果您将和定义为任何不同的内容\foo,那么您将获得预期的行为。\bar\zot

\documentclass{article}
\let\foo=F
\let\bar=B
\let\zot=Z

\makeatletter
  \newcommand{\startfoo}{             % Macro Entry
    \@ifnextchar\foo                  %
      { START FOO \\ \@nextfoo }      % nextchar == \foo, start FOO section
      { } }                           % nextchar != \foo
  \newcommand\@nextfoo[2]{ #2 \\      % Display a foo. #1 is \foo
    \@ifnextchar\bar                  %
      { START BAR \\ \@nextbar }      % nextchar == \bar, start BAR section
      { \@checkfoo } }                % nextchar != \bar
  \newcommand\@nextbar[2]{ #2 \\      % Display a bar. #1 is \bar
    \@ifnextchar\zot                  %
      { START ZOT \\ \@nextzot }      % nextchar == \zot, start ZOT section
      { \@checkbar } }                % nextchar != \zot
  \newcommand\@nextzot[2]{ #2 \\      % Display a zot, #1 is \zot
    \@checkzot }                      %
  \newcommand\@checkzot{              % Check if nextchar is \zot
    \@ifnextchar\zot                  %
      { \@nextzot }                   % nextchar == \zot, continue section
      { \@endzot } }                  % nextchar != \zot
  \newcommand\@checkbar{              % Check if nextchar is \bar
    \@ifnextchar\bar                  %
      { \@nextbar }                   % nextchar == \bar, continue section
      { \@endbar } }                  % nextchar != \bar
  \newcommand\@checkfoo{              % Check if nextchar is \foo
    \@ifnextchar\foo                  %
      { \@nextfoo }                   % nextchar == \foo, continue section
      { \@endfoo } }                  % nextchar != \foo
  \newcommand\@endzot{ END ZOT \\     % End ZOT section
    \@checkbar}                       % Go check on BAR
  \newcommand\@endbar{ END BAR \\     % End BAR section
    \@checkfoo}                       % Go check on FOO
  \newcommand\@endfoo{ END FOO \\  }  % End FOO section, We're done
\makeatother

\begin{document}  % Expected Output:  | Actual Output:

  \startfoo       %
  \foo{foo1}      % START FOO         | START FOO
                  % foo1              | foo1
    \bar{bar1}    % START BAR         | START BAR
                  % bar1              | bar1
      \zot{zot1}  % START ZOT         | START ZOT
                  % zot1              | zota
      \zot{zota}  % zota              | zota
                  % END ZOT
                  % END BAR
  \foo{foo2}      % foo2              | foo2
                  % END FOO           | END ZOT
                  %                   | END BAR
                  %                   | END FOO
  THUD            % THUD              | THUD

\end{document}    %

定义并提供您需要的任何布局而不是让每个命令都以这种方式向前看会简单得多\foo\bar\zot

相关内容