为什么我对“\item”的重新定义没有意识到它已经传递了一个可选参数?

为什么我对“\item”的重新定义没有意识到它已经传递了一个可选参数?

我想\item暂时重新定义,但也想让它表现得像普通的 \item应该。所以,我认为通过严格遵循 的定义\item,我会得到我想要的结果。但是以下 MWE 无法识别可选参数。

\documentclass{article}
\pagestyle{empty}
\begin{document}

\begin{enumerate}
\begingroup
  \makeatletter
  \def\item{\@ifnextchar [\@item{\@noitemargtrue \@item[\@itemlabel]} \rule[-2ex]{0.8pt}{5ex}\endgroup}%%'
  \makeatother
\item[test]   A
\item B
\item C
\item D
\end{enumerate}

\end{document}

为什么会失败?

答案1

看一下\@ifnextchar(取自latex.ltx):

\long\def\@ifnextchar#1#2#3{%
  \let\reserved@d=#1%
  \def\reserved@a{#2}%
  \def\reserved@b{#3}%
  \futurelet\@let@token\@ifnch}

它将三个参数存储在由 标识的三个单独的宏中\reserved@?。然后,它调用\futurelet\@let@token\@ifnch。 的行为\futurelet(如中所述)在哪里可以找到\futurelet有关 的恶劣行为的记录?) 是:

TeX 还允许构造\futurelet\cs<token1><token2>,其效果为\let\cs = <token2><token1><token2>

在一个简化的情况下,重新\item定义任何事物后续\@ifnextchar施工会造成问题,以后<token2>就不会再有[


如何解决这个限制?不要\@ifnextchar直接使用。下面是一种使用传统方法的简单方法\renewcommand

在此处输入图片描述

\documentclass{article}
\pagestyle{empty}
\begin{document}

\begin{enumerate}
  \begingroup
  \makeatletter
  \let\olditem\item
  \renewcommand{\item}[1][\@empty]{%
    \ifx#1\@empty
      \olditem%
    \else
      \@item[#1] \rule[-2ex]{0.8pt}{5ex}\endgroup%
    \fi}%
  \makeatother
  \item[abc]   A
  \item B
  \item[xyz] C
  \item D
\end{enumerate}

\end{document}

答案2

作为沃纳指出,您不能在 '之后' 添加任何内容\@ifnextchar。您还需要确保 后面\@item跟着[,因此您不能只将规则添加到结果的两个分支\@ifnextchar。同时,如果可能的话,我会避免使用\begingroup外部宏和\endgroup内部宏。这导致了以下形式的解决方案:

\documentclass{article}
\usepackage{etoolbox}
\pagestyle{empty}
\tracingpatches
\begin{document}

\begin{enumerate}
  \let\origitem\item
    \makeatletter
  \let\orig@item\@item
  \patchcmd{\@item}{\ignorespaces}
    {%
      \rule[-2ex]{0.8pt}{5ex}%
      \let\@item\orig@item
      \ignorespaces
    }
    {}{}
  \def\item{%
    \let\item\origitem
    \@ifnextchar [%]
      \@item
      {\@noitemargtrue \@item[\@itemlabel]}%
  }
  \makeatother
\item[test] A
\item B
\item C
\item D
\end{enumerate}

\end{document}

请注意,使用\item将恢复原始定义,使用 也是如此\@item,但没有组。

答案3

虽然在最初的问题中没有提到,但最终我想要一些更动态的东西:也就是说,我希望重新定义\item能够持续存在,而不仅仅是第一次出现。我还想要一些行为符合预期如果嵌套在另一个列表中。

在看到@werner 和@wright 的解决方案之前,我最终得出的结论是:

\documentclass{article}
\makeatletter
\let\ae@original@item\item
\newenvironment{myenum}[1]
  {\begin{enumerate}
     \let\item\ae@original@item
     \def\ae@how@far@advanced{1}%%'
     \newif\ifae@unfinished@
     \begingroup
     \ae@unfinished@true
     \def\ae@item[##1]{%%'
       \@item[##1] \rule[-2ex]{0.8pt}{5ex}%%'
       \ifnum\ae@how@far@advanced=#1\relax
         \ae@unfinished@false\endgroup
       \fi
       \edef\ae@how@far@advanced{\number\numexpr\ae@how@far@advanced+1\relax}\ignorespaces}
     \def\item{\@ifnextchar [%%]
        \ae@item{\@noitemargtrue \ae@item[\@itemlabel]}}%%'
  }%%'
  {\ifae@unfinished@\endgroup\fi
   \end{enumerate}}
\makeatother

\begin{document}

\begin{myenum}{5}
  \item A
  \item [test]   B
  \item C
    \begin{myenum}{2}
      \item Bc
      \item A
      \item C
      \item D
    \end{myenum}
  \item D
  \item E
  \item F
\end{myenum}

\end{document}

在此处输入图片描述

我想那\ae@how@far@advanced可能真的是一个计数器。但是,我还是需要一些东西,因为我计算的是 的出现次数,\item而不是 (和家族) 的值,使用enumi时不会提前。\item[...]

我喜欢提供的两种解决方案。@wright 的解决方案很好地展示了如何避免使用\begingroup\endgroup。但是,我宁愿不调用另一个包。@werner 的解决方案非常优雅,它避免了调用\@ifnextchar并设法测试空的争论。

我选择发布自己的解决方案,因为我最终看到了自己的

  1. 误解何时以及什么正在扩大,
  2. 误解是\@ifnextchar永远不会看到我的宏之后的下一个字符,因为我已经抛出了很多噪音挡住路。

但我能够找到解决方案\@ifnextchar并正确地重定向事物。

相关内容