嵌套和[恢复]时如何计算枚举中的项目数

嵌套和[恢复]时如何计算枚举中的项目数

作为问题提前清点并使用物品数量提到,我也有同样的需求,但我的更先进。

这里我有一些枚举,其中一些以[resume]参数开头,其中一些有嵌套枚举。我想计算每个枚举的第一级项的数量。例如

在此处输入图片描述

我写了以下代码:

\documentclass{article}
\usepackage{enumitem}
\makeatletter
\newcounter{totalitems}
\newcounter{beginitems}
\newcounter{enditems}
\let\@numerate\enumerate

\def\enumerate{%
    \setcounter{beginitems}{\arabic{enumi}}
    \@numerate
}
\let\end@numerate\endenumerate
\def\endenumerate{
    \end@numerate%
    \setcounter{enditems}{\arabic{enumi}}
    \setcounter{totalitems}{\numexpr \c@enditems - \c@beginitems \relax}
}
\makeatother
\begin{document}
    \begin{enumerate}
        \item one
        \item two
    \end{enumerate}
    total = \thetotalitems, begin = \thebeginitems, end = \theenditems

    \begin{enumerate}[resume]
        \item three
        \begin{enumerate}
            \item a nested enumerate
        \end{enumerate}
    \end{enumerate}
    total = \thetotalitems, begin = \thebeginitems, end = \theenditems

    \begin{enumerate}
        \item one 
        \item two 
    \end{enumerate}
    total = \thetotalitems, begin = \thebeginitems, end = \theenditems
    
\end{document}

它产生了:

在此处输入图片描述

我们可以看到有些事情出了问题

  1. 当有嵌套枚举时,beginitems计数器错误
  2. 当没有[resume]参数时,beginitems计数器不会被重置为0。

我应该在哪里改变才能达到我的要求?

答案1

您需要推迟\setcounter{beginitems}{\arabic{enumi}}enumerate环境启动之后,因此本质上分别是之后\begin{enumerate}或之后\begin{enumerate}[resume]。您需要考虑到\begin{environment}可能有一个可选参数。

我不完全确定,但我认为,在这里使用\LetLtxMacro而不是普通的\let而是\renewcommand\def安全一些:

\documentclass{article}
\usepackage{enumitem}

\makeatletter
\newcounter{totalitems}
\newcounter{beginitems}
\newcounter{enditems}

\usepackage{letltxmacro}
\LetLtxMacro{\@numerate}{\enumerate}
\LetLtxMacro{\end@numerate}{\endenumerate}

\renewcommand{\enumerate}[1][]{%
    \@numerate[#1]%
    \setcounter{beginitems}{\arabic{enumi}}
}
\renewcommand{\endenumerate}{
    \end@numerate%
    \setcounter{enditems}{\arabic{enumi}}
    \setcounter{totalitems}{\numexpr \c@enditems - \c@beginitems \relax}
}
\makeatother

\begin{document}

    \begin{enumerate}
        \item one
        \item two
    \end{enumerate}
    total = \thetotalitems, begin = \thebeginitems, end = \theenditems

    \begin{enumerate}[resume]
        \item three
        \begin{enumerate}
            \item a nested enumerate
        \end{enumerate}
    \end{enumerate}
    total = \thetotalitems, begin = \thebeginitems, end = \theenditems

    \begin{enumerate}
        \item one 
        \item two 
    \end{enumerate}
    total = \thetotalitems, begin = \thebeginitems, end = \theenditems
    
\end{document}

在此处输入图片描述

现在,您仍然可能遇到这样的问题:每次启动环境时计数器都会受到影响enumerate,当环境嵌套时,这可能会导致错误的结果enumerate。我没有彻底测试这一点。

答案2

感谢@JohnKormylo 和@JasperHabicht

来自@JasperHabicht 的方法

\enit@depth我找到了一个显示枚举深度的控制序列。所以我可以用\if它来测试我是否在第一层,就像这样:

\renewcommand{\enumerate}[1][]{%
    \@numerate[#1]%
    \ifnum\enit@depth=\@ne
        \setcounter{beginitems}{\arabic{enumi}}
    \fi
}
\renewcommand{\endenumerate}{
    \ifnum\enit@depth=\@ne
        \setcounter{enditems}{\arabic{enumi}}
        \setcounter{totalitems}{\numexpr \c@enditems - \c@beginitems \relax}
    \fi
    \end@numerate%
}

如果这样做,当我们不在第一层时,\setcounters 将不起作用。完整代码如下:

\documentclass{article}
\usepackage{enumitem}

\makeatletter
\newcounter{totalitems}
\newcounter{beginitems}
\newcounter{enditems}

\usepackage{letltxmacro}
\LetLtxMacro{\@numerate}{\enumerate}
\LetLtxMacro{\end@numerate}{\endenumerate}

\renewcommand{\enumerate}[1][]{%
    \@numerate[#1]%
    \ifnum\enit@depth=\@ne
        \setcounter{beginitems}{\arabic{enumi}}
    \fi
}
\renewcommand{\endenumerate}{
    \ifnum\enit@depth=\@ne
        \setcounter{enditems}{\arabic{enumi}}
        \setcounter{totalitems}{\numexpr \c@enditems - \c@beginitems \relax}
    \fi
    \end@numerate%
}
\makeatother

\begin{document}

    \begin{enumerate}
        \item one
        \item two
    \end{enumerate}
    total = \thetotalitems, begin = \thebeginitems, end = \theenditems

    \begin{enumerate}[resume]
        \item three
        \begin{enumerate}
            \item a nested enumerate
        \end{enumerate}
    \end{enumerate}
    total = \thetotalitems, begin = \thebeginitems, end = \theenditems

    \begin{enumerate}
        \item one 
        \item two 
    \end{enumerate}
    total = \thetotalitems, begin = \thebeginitems, end = \theenditems
    
\end{document}

在此处输入图片描述

我的新方法

在这个方法中,我更新了\item命令,让计数器在\item出现在顶层时加1:

  1. 首先,我们创建一个 newif\ifenum来测试我们是否处于顶层,我们创建一个计数器并保存宏:
\newif\ifenum
\newcounter{totalitems}

\usepackage{letltxmacro}
\LetLtxMacro{\@numerate}{\enumerate}
\LetLtxMacro{\end@numerate}{\endenumerate}
\LetLtxMacro{\it@m}{\item}
  1. 用于xparse更新\item命令,当此命令\item位于顶层时,步进计数器totalitems。我们还可以让它具有一些新功能,例如\item*不计数、\item[<paramater>]对于用户定义的标签,除了先前的定义外还进行计数、\item*[paramater]对于用户定义的标签不进行计数:
\DeclareDocumentCommand{\item}{ s o }{
    \IfNoValueTF{#2}{\it@m}{\it@m[#2]}  
    \ifenum
        \IfBooleanT{#1}{\addtocounter{totalitems}{-1}}%
        \stepcounter{totalitems}
    \fi
}
  1. 然后我们判断什么时候应该将 置为真\ifenum: after \begin{enumerate},我们测试是否处于顶层,如果是,则设置totalitems为 0,并设置\enumtrue,如果不是,则设置\enumfalse; before 则\end{enumerate}相反。嵌套列表何时出现并不重要,因为\ifenum在第二层或更深的层开始时, 总是会变为假。
\renewcommand{\enumerate}[1][]{%
    \@numerate[#1]
    \ifnum\enit@depth=\@ne
        \setcounter{totalitems}{0}
        \enumtrue
    \else
        \enumfalse
    \fi
}
\renewcommand{\endenumerate}{
    \ifnum\enit@depth=\@ne 
        \enumfalse
    \else
        \enumtrue
    \fi
    \end@numerate%
}

现在我们可以得到顶级项目的数量:

\documentclass{article}
\usepackage{enumitem}
\newif\ifenum
\makeatletter
\newcounter{totalitems}

\usepackage{letltxmacro}
\LetLtxMacro{\@numerate}{\enumerate}
\LetLtxMacro{\end@numerate}{\endenumerate}
\LetLtxMacro{\it@m}{\item}

\DeclareDocumentCommand{\item}{ s o }{
    \IfNoValueTF{#2}{\it@m}{\it@m[#2]}
    \ifenum
        \IfBooleanT{#1}{\addtocounter{totalitems}{-1}}%
        \stepcounter{totalitems}
    \fi
}

\renewcommand{\enumerate}[1][]{%
    \@numerate[#1]
    \ifnum\enit@depth=\@ne
        \setcounter{totalitems}{0}
        \enumtrue
    \else
        \enumfalse
    \fi
}
\renewcommand{\endenumerate}{
    \ifnum\enit@depth=\@ne 
        \enumfalse
    \else
        \enumtrue
    \fi
    \end@numerate%
}

\makeatother

\begin{document}

    \begin{enumerate}
        \item one
        \item two
    \end{enumerate}
    total = \thetotalitems

    \begin{enumerate}[resume]
        \item three
        \begin{enumerate}
            \item a nested enumerate
            \item* a star nest
        \end{enumerate}
        \item item after nested
    \end{enumerate}
    total = \thetotalitems

    \begin{enumerate}
        \item* a star version 
        \item[paramater] the only item which is counted
        \item*[paramater] a star version with paramater
    \end{enumerate}
    total = \thetotalitems
    
\end{document}

在此处输入图片描述

相关内容