我在枚举环境中有一个包含近 300 个项目的列表
\documentclass{article}
\begin{document}
\begin{enumerate}
\item This is the second item.
\item This is the third item.
\item This is the first item.
\end{enumerate}
\end{document}
我想以不同的顺序列出(第三项应该是第一项,第一项应该是第二项,第二项应该是第三项。)我希望输出如下所示:
有没有办法将参数 [3, 1, 2] 传递给某个函数并自动按顺序排版列表,而不是手动排版?谢谢。
编辑:我看到我们过去xpatch
使用旧的 item。但是,在我的文档中,我重新定义了 item,如下所示:
\newcounter{myenumi}
\renewcommand{\themyenumi}{\textbf{Example \thesection.\arabic{myenumi}.}}
\newenvironment{exenumerate}{%
% stuff for beginning of environment goes here
\setlength{\parindent}{0pt}% don't indent paragraphs
\setcounter{myenumi}{0}% restart numbering
\bigskip% skip a line
\renewcommand{\item}{% new definition of item
\par %start a new line
\medskip
\refstepcounter{myenumi}% advance counter
\noindent \makebox[8em][l]{\themyenumi}% print counter to width of 3em, aligned to left
}% end of definition of item
}{% at end of environment
\par% start new paragraph
\bigskip% skip a line
\noindent% don't indent new paragraph
\ignorespacesafterend% ignore spaces after environment
}
沃纳的解决方案仍然有效吗?
答案1
尽管 LaTeX 不是处理此类问题的最佳语言,但这里有一个潜在的解决方案。它使用包pgffor
。
首先,我们加载pgffor
包并定义一个计数器,用于跟踪列表中有多少个项目。
\usepackage{pgffor}
\newcounter{SortListTotal}
然后,我们定义一个存储列表项的命令。
\newcommand{\sortitem}[2]{\expandafter\def\csname SortListItem#1\endcsname{#2}\stepcounter{SortListTotal}}
第一个参数\sortitem
是项目的编号;第二个参数是项目文本。
现在,我们定义一个命令来打印出列表。此命令还会重置计数器,为新的排序列表做好准备。
\newcommand{\printsortlist}{\foreach\currentlistitem in{1,2,...,\value{SortListTotal}}{\item[\currentlistitem]\csname SortListItem\currentlistitem\endcsname}\setcounter{SortListTotal}{0}}
这些命令的使用方法如下:
\begin{enumerate}
\sortitem{3}{This is the third item.}
\sortitem{4}{This is the fourth item.}
\sortitem{1}{This is the first item.}
\sortitem{2}{This is the second item.}
\printsortlist
\end{enumerate}
或者,如果您想在存储项目后指定顺序:
\makeatletter
\newcounter{SortListTotal}
\newcommand{\sortitem}[1]{\stepcounter{SortListTotal}\expandafter\def\csname SortItem\arabic{SortListTotal}\endcsname{#1}}
\newcommand{\printsortlist}[1]{\@for\currentitem:=#1\do{\item\csname SortItem\currentitem\endcsname}\setcounter{SortListTotal}{0}}
\makeatother
这些命令的使用方法如下:
\begin{enumerate}
\sortitem{This is the third item.}
\sortitem{This is the fourth item.}
\sortitem{This is the first item.}
\sortitem{This is the second item.}
\printsortlist{3,4,1,2}
\end{enumerate}
答案2
这是一个满足您需求的实现:
\documentclass{article}
\usepackage{environ,etoolbox}% http://ctan.org/pkg/{environ,etoolbox}
\makeatletter
\newcounter{orderenum}\newcounter{listcount}[orderenum]%\newcounter{listtotal}[orderenum]
\let\olditem\item% Store regular \item macro
\NewEnviron{orderenum}[1][\relax]{%
\stepcounter{orderenum}% New orderenum environment (also resets listcount)
\def\optarg{#1}% Store optional argument
\expandafter\ifx\optarg\relax% A normal list
\enumerate\BODY\endenumerate% Process environment
\else% A reordered list
\g@addto@macro{\BODY}{\item\relax\item}% Used to delimit the items; last item identified by \item\relax\item
\def\item##1\item{% Redefine \item to capture contents
\def\optarg{##1}%\show\optarg%
\expandafter\ifx\optarg\relax\else% Last item not reached
\stepcounter{listcount}% Next item being processed
\csgdef{orderenum@\theorderenum @\thelistcount}{##1}% Store item in control sequence
\expandafter\item% Recursively continue processing items
\fi
}
\BODY% Process environment (save items)
\renewcommand*{\do}[1]{\olditem \csname orderenum@\theorderenum @##1\endcsname}% Print each item in order
\enumerate\docsvlist{#1}\endenumerate% Process items
\fi%
}
\makeatother
\begin{document}
\begin{orderenum}
\item This is the second item.
\item This is the third item.
\item This is the first item.
\end{orderenum}
\begin{orderenum}[3,1,2]
\item This is the second item.
\item This is the third item.
\item This is the first item.
\end{orderenum}
\end{document}
整个列表被“处理”两次。第一次,每个\item
都存储在基于数字的宏中。第二次,按照 的可选参数中逗号分隔列表定义的顺序逐个调用这些项目orderenum
。
这种方法的优点是您可以使用常规的\item
-interface(而不必使用(例如))\myitem{...}
来捕获项目。此外,还会进行测试以确保没有可选参数默认为常规列表。从技术上讲,您还可以指定要打印的列表中项目的子集。
更新:对于使用amsthm
在的proof
环境中orderenum
,需要了解proof
实际上是设置为单项列表。因此,它使用\item
,orderenum
重新定义 来捕获其内容。因此,我们需要一个围绕它的策略。由于 定义的性质proof
,xpatch
提供了一种修补方法。应使用以下代码替换上述示例:
\usepackage{environ,xpatch}% http://ctan.org/pkg/{environ,xpatch}
\xpatchcmd{\proof}{\item}{\olditem}{}{}% Patch proof environment to use \olditem
现在可以成功使用,比如说
%...
\begin{orderenum}[3,1,2]
\item This is the second item.
\item This is the third item.
\begin{proof} some proof \end{proof}
\item This is the first item.
\begin{proof} another proof \end{proof}
\end{orderenum}
%...
exenumerate
将问题的更新添加到有序列表中并不困难。以下是包含您从枚举示例中定义的 MWE :
\documentclass{article}
\usepackage{environ,etoolbox}% http://ctan.org/pkg/{environ,etoolbox}
\makeatletter
\newcounter{myenumi}
\renewcommand{\themyenumi}{\textbf{Example \thesection.\arabic{myenumi}.}}
\newenvironment{exenumerate}{%
% stuff for beginning of environment goes here
\setlength{\parindent}{0pt}% don't indent paragraphs
\setcounter{myenumi}{0}% restart numbering
\bigskip% skip a line
\renewcommand{\olditem}{% new definition of item
\par %start a new line
\medskip
\refstepcounter{myenumi}% advance counter
\noindent \makebox[8em][l]{\themyenumi}% print counter to width of 3em, aligned to left
}% end of definition of item
}{% at end of environment
\par% start new paragraph
\bigskip% skip a line
\noindent% don't indent new paragraph
\ignorespacesafterend% ignore spaces after environment
}
\newcounter{orderenum}\newcounter{listcount}[orderenum]%\newcounter{listtotal}[orderenum]
\let\olditem\item% Store regular \item macro
\NewEnviron{orderenum}[1][\relax]{%
\stepcounter{orderenum}% New orderenum environment (also resets listcount)
\def\optarg{#1}% Store optional argument
\expandafter\ifx\optarg\relax% A normal list
\enumerate\BODY\endenumerate% Process environment
\else% A reordered list
\g@addto@macro{\BODY}{\item\relax\item}% Used to delimit the items; last item identified by \item\relax\item
\def\item##1\item{% Redefine \item to capture contents
\def\optarg{##1}%\show\optarg%
\expandafter\ifx\optarg\relax\else% Last item not reached
\stepcounter{listcount}% Next item being processed
\csgdef{orderenum@\theorderenum @\thelistcount}{##1}% Store item in control sequence
\expandafter\item% Recursively continue processing items
\fi
}
\BODY% Process environment (save items)
\renewcommand*{\do}[1]{\olditem \csname orderenum@\theorderenum @##1\endcsname}% Print each item in order
\exenumerate\docsvlist{#1}\endexenumerate% Process items
\fi%
}
\makeatother
\begin{document}
\begin{orderenum}
\item This is the second item.
\item This is the third item.
\item This is the first item.
\end{orderenum}
\begin{orderenum}[3,1,2]
\item This is the second item.
\item This is the third item.
\item This is the first item.
\end{orderenum}
\end{document}
答案3
这里有两种简单的方法。一种依赖于datatool
;另一种依赖于手动强制enumerate
打印您选择的任意数量的项目。
\documentclass{article}
% To create an external .csv file
\usepackage{filecontents}
\begin{filecontents}{\jobname.csv}
Order, Text
3, Third item
1, First item
2, Second item
4, Fourth item
\end{filecontents}
\usepackage{datatool}
\DTLloaddb{externalcsv}{\jobname.csv}
\begin{document}
An example using \verb+datatool+ and an external \verb+.csv+ file:
\begin{enumerate}
\DTLforeach{externalcsv}{\myorder=Order, \mytext=Text}{\item[\myorder.] \mytext}
\end{enumerate}
Or a very simple approach:
\begin{enumerate}
\item[2.] This is the second item.
\item[3.] This is the third item.
\item[1.] This is the first item.
\setcounter{enumi}{3}
\item This is the fourth item.
\end{enumerate}
\end{document}
或者,如果您希望.csv
按(比如说)“顺序”字段自动排序,您可以添加\DTLsort{Order=ascending}{externalcsv}%
;即:
\begin{enumerate}
\DTLsort{Order=ascending}{externalcsv}%
\DTLforeach{externalcsv}{\myorder=Order, \mytext=Text}{\item[\myorder.]\mytext}
\end{enumerate}
...至于如何从现有enumerate
输入创建“编号”列表,有多种方法。使用sed
您可以只取枚举行(因此每行都是\item This is ...
),将其命名为 input.csv,然后执行以下操作:
sed = input.csv | sed 'N;s/\n\\item/,/' > output.csv
这应该产生output.csv
,其中每一行都是
<num>, This is the <num>th item
但这将不是是一个排序列表:它根据输入文件的实际行号分配行号。
答案4
其他人给出了答案,告诉你如何在 (La)TeX 中做到这一点,但考虑到你有 300 个条目,这可能会大大减慢文档编译速度。这是另一种可能性。
首先,将所有物品保存在datatool
数据库如下:
\documentclass{article}
\usepackage{morewrites}
\usepackage{etoolbox}
\usepackage{datatool}
\usepackage{probsoln}% for \long@collect@body
\newwrite\tmpwrite
\newcommand{\dbname}{enumdata}
\DTLnewdb{\dbname}
\makeatletter
\def\itemreplacement{%
\expandafter\@gobble\string\}^^J%
\string\DTLnewrow{\dbname}^^J%
\string\DTLnewdbentry{\dbname}{Text}%
\expandafter\@gobble\string\{%
}%
\bgroup
\obeyspaces
\gdef\activespace{ }
\egroup
\newcount\envcount
\long\def\parsecontents#1{%
\def\thisval{#1}%
\ifx\thisval\@nnil
\let\next\relax
\else
\ifx\thisval\@empty
\else
\ifx#1\par
\appto\enumcontents{\DTLpar}%
\else
\ifx#1\begin
\advance\envcount by 1\relax
\appto\enumcontents{#1}%
\else
\ifx#1\end
\advance\envcount by -1\relax
\appto\enumcontents{#1}%
\else
\ifx#1\item
\ifnum\envcount > 0\relax
\appto\enumcontents{#1}%
\else
\eappto\enumcontents{\itemreplacement}%
\fi
\else
\ifx\thisval\activespace
\appto\enumcontents{ }%
\else
\dtl@ifsingle{#1}%
{%
\appto\enumcontents{#1}%
}%
{%
\appto\enumcontents{{#1}}%
}%
\fi
\fi
\fi
\fi
\fi
\fi
\let\next\parsecontents
\fi
\next
}
\newcommand{\gatherenumcontents}[1]{%
\parsecontents#1\@nil
}
\newenvironment{orderenumerate}%
{%
\envcount=0\relax
\def\enumcontents{}%
\obeyspaces
\long@collect@body\gatherenumcontents
}%
{%
\@onelevel@sanitize\enumcontents
\let\gobble\@gobble
\immediate\openout\tmpwrite=\jobname.tmp
\immediate\write\tmpwrite{%
\string\gobble
\expandafter\@gobble\string\{\enumcontents
\expandafter\@gobble\string\}}%
\immediate\closeout\tmpwrite
\DTLcleardb{\dbname}%
\input{\jobname.tmp}%
\DTLprotectedsaverawdb{\dbname}{\dbname.dbtex}%
}
\makeatother
\begin{document}
\begin{orderenumerate}
\item This is the second item.
\begin{proof}
This is the proof of the second item.
\end{proof}
\item This is the third item. This item has a list:
\begin{enumerate}
\item First item.
\item Second item.
\end{enumerate}
\begin{proof}
This is the proof of the third item.
\end{proof}
\item This is the first item.
\begin{proof}
This is the proof of the first item.
\end{proof}
\end{orderenumerate}
% force shipout to ensure .dbtex file is written:
\mbox{}\newpage
\end{document}
这将创建一个名为 的新文件enumdata.dbtex
。(您可以通过修改 的定义来更改此文件\dbname
。)现在,您无需在.tex
文件中编辑项目,而是可以在datatooltk
。请注意,上述示例不产生任何文本,它只是收集数据并将其保存到文件中.dbtex
。(我刚刚上传datatooltk
到 CTAN,因此可能需要几天时间才能传播到所有镜像。)
上述示例创建的文件.dbtex
加载后如下所示datatooltk
:
您可以通过拖动行号按钮来上下移动行,也可以添加可用作排序键的新列。例如:
datatooltk
现在可以通过菜单对数据进行排序Tools
。双击单元格即可打开单元格编辑器,从而编辑单个项目:
完成后,保存数据库,现在您可以像这样显示文档中的项目:
\documentclass{article}
\usepackage{amsthm}
\usepackage{datatool}
% Load data:
\input{enumdata.dbtex}
\begin{document}
\begin{enumerate}\let\DTLpar\par
\DTLforeach*{\dtllastloadeddb}{\Text=Text}%
{%
\item \Text
}%
\end{enumerate}
\end{document}
文档现在如下所示:
现在,当您处理文档的其余部分时,您的速度不会因为 TeX 处理未分类的项目而减慢。