作为问题提前清点并使用物品数量提到,我也有同样的需求,但我的更先进。
这里我有一些枚举,其中一些以[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}
它产生了:
我们可以看到有些事情出了问题
- 当有嵌套枚举时,
beginitems
计数器错误 - 当没有
[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%
}
如果这样做,当我们不在第一层时,\setcounter
s 将不起作用。完整代码如下:
\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:
- 首先,我们创建一个 newif
\ifenum
来测试我们是否处于顶层,我们创建一个计数器并保存宏:
\newif\ifenum
\newcounter{totalitems}
\usepackage{letltxmacro}
\LetLtxMacro{\@numerate}{\enumerate}
\LetLtxMacro{\end@numerate}{\endenumerate}
\LetLtxMacro{\it@m}{\item}
- 用于
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
}
- 然后我们判断什么时候应该将 置为真
\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}