错误使用 \@nil 或 \if \ifx?

错误使用 \@nil 或 \if \ifx?

我尝试构建一个名为的宏mlist,它有两种用途:

\mlist{...} will redefine the content of \clist
\mlist+{...} will apppend something to \clist

我尝试以下代码失败了。我的代码有什么问题?

代码:

\documentclass{article}

\makeatletter
\long\def\mlist#1#2{
  \if#1+\relax \edef\clist{\clist,#2}%append #2 to \clist
  \else
    \ifx\@nil#2
    \def\clist{#1}#2 %redefine \clist
    \else
    \mlist#1\@nil
    \fi
  \fi
}
\makeatother

\begin{document}
\def\clist{}
\mlist{cc}\clist\par% "cc" expected
\mlist+{dd}\clist\par   % "cc,dd" expected
\end{document}

答案1

您的\mlist宏有两个参数\mlist{cc}\clist,并且#1是,cc而是。这绝对不是您想要的。#2\clist

您想要的\@ifnextchar是,提前查找下一个标记,并允许您根据找到的内容做出选择。

\documentclass{article}

\makeatletter
\newcommand{\mlist}{\@ifnextchar+{\mlist@add}{\mlist@def}}
\newcommand{\mlist@def}[1]{\def\clist{#1}}
\newcommand{\mlist@add}[2]{% #1 is +, discard it
  \expandafter\def\expandafter\clist\expandafter{\clist,#2}%
}
\makeatother

\begin{document}

\def\clist{}
\mlist{cc}\clist\par% "cc" expected
\mlist+{dd}\clist\par   % "cc,dd" expected

\end{document}

在此处输入图片描述

如果要添加在前面,策略可以如下:

\documentclass{article}

\makeatletter
\newcommand{\mlist}{\@ifnextchar+{\mlist@add}{\mlist@def}}
\newcommand{\mlist@def}[1]{\def\clist{#1}}
\newcommand{\mlist@add}[2]{% #1 is +, ignore it
  \expandafter\mlist@prepend\expandafter{\clist}{#2}%
}
\newcommand{\mlist@prepend}[2]{\def\clist{#2,#1}}
\makeatother

\begin{document}

\def\clist{}
\mlist{cc}\clist\par % "cc" expected
\mlist+{dd}\clist\par   % "dd,cc" expected

\end{document}

在此处输入图片描述

你的代码哪里错了?

在您的第一个调用中,\mlist{cc}\clist您有ccas#1\clistas #2。这已经非常危险了,因为您不知道 之后会发生什么\mlist{...}。无论如何,您的\if#1+测试结果是正确的,因为它是

\if cc+

所以你得到了

+\relax\edef\clist{\clist,\clist}

这是你不想要的,对吧?如果我们反转,因为\if+#1\relax测试结果会是假的,所以\else分支被采用,所以你会得到

\ifx\@nil\clist\def\clist{cc}\clist\else\mlist#1\@nil\fi\fi

(最后一个\fi将在稍后通过宏扩展被删除)。此测试返回 false,因此您得到

\mlist cc\@nil\fi\fi

请注意,您丢失了括号。因此,让我们尝试通过添加括号来修复。现在您得到了

\if+cc\relax\edef\clist{\clist,\@nil}\else\ifx\@nil\@nil\def\clist{cc}\@nil\else\mlist{cc}\@nil\fi\fi\fi\fi

(我还删除了虚假的空格)。第一个测试是错误的,所以你得到

\ifx\@nil\@nil\def\clist{cc}\@nil\else\mlist{cc}\@nil\fi\fi\fi\fi

这次测试是正确的,其余的一切都会被忽略。

现在我们来检查第二个调用,即

\mlist+{dd}

#1+#2dd,因此(使用已经提到的修复)你得到

\if++\relax\edef\clist{\clist,dd}\else...\fi

之后的部分\else无关紧要,因为测试结果是正确的。

所以应该是

\long\def\mlist#1#2{%
  \if#1+\relax \edef\clist{\clist,#2}%append #2 to \clist
  \else
    \ifx\@nil#2%
    \def\clist{#1}#2%redefine \clist
    \else
    \mlist{#1}\@nil
    \fi
  \fi
}

但它不是最好的方法。请仔细观察角色的位置%

可能更好的方法

我使用可选参数(用于管理更多列表)和“修饰”来定义\mlist。请参阅示例,但本质上

\mlist<{a}
\mlist>{b}

分别附加或添加到列表。

我还添加了从列表中提取项目并计算其长度的代码。

\documentclass{article}

\ExplSyntaxOn
\NewDocumentCommand{\mlist}{ O{default} e{<>} }
 {
  \clist_if_exist:cF { l_lyl_mlist_#1_clist } { \clist_new:c { l_lyl_mlist_#1_clist } }
  \lyl_mlist_append:nnn { #1 } { #2 } { #3 }
 }

\NewExpandableDocumentCommand{\mlistitem}{ O{default} m }
 {
  \clist_item:cn { l_lyl_mlist_#1_clist } { #2 }
 }

\NewExpandableDocumentCommand{\mlistcount}{ m }
 {
  \clist_count:c { l_lyl_mlist_#1_clist }
 }

\NewDocumentCommand{\mlistprint}{ O{default} }
 {
  \clist_use:cn { l_lyl_mlist_#1_clist } { , }
 }

\cs_new_protected:Nn \lyl_mlist_append:nnn
 {
  \clist_put_left:cx { l_lyl_mlist_#1_clist }
   {
    \tl_if_novalue:nF { #2 } { \exp_not:n { #2 } }
   }
  \clist_put_right:cx { l_lyl_mlist_#1_clist }
   {
    \tl_if_novalue:nF { #3 } { \exp_not:n { #3 } }
   }
 }

\ExplSyntaxOff

\begin{document}

\mlist>{cc}

\mlistprint % "cc" expected

\mlist<{bb}

\mlistprint % "bb,cc" expected

\mlist>{dd}

\mlistprint % "bb,cc,dd" expected

\mlist<{a}>{e}

\mlistprint % "a,bb,cc,dd,e" expected

\mlistcount{default} % 5 expected

\mlistitem{4} % "dd" expected

\mlist[new]

\mlist[new]<{xyz}
\mlist[new]>{abc}

\mlistprint[new] % "xyz,abc" expected

\mlistcount{new} % 2 expected

\mlistitem[new]{1} % "xyz" expected

\end{document}

在此处输入图片描述

答案2

你可以简单地使用 TeX 原语来完成你的任务:

\def\mlist {\futurelet\next\mlistA}
\def\mlistA {\ifx\next+\expandafter\mlistB \else\expandafter\mlistC \fi}
\def\mlistB +#1{\expandafter\def\expandafter\clist\expandafter{\clist,#1}}
\def\mlistC #1{\def\clist{#1}}
\def\clist{}

% test:
\mlist{cc}\clist\par% "cc" expected
\mlist+{dd}\clist\par   % "cc,dd" expected

相关内容