我有以下设置,我有一个包含一些定义的文件,其中一个是项目列表:
\newcommand{\mylist}{
\item first item
\item second item
\item third item
}
我想用\mylist
两种不同的方式使用,首先
\begin{itemize}
\mylist
\end{itemize}
第二,
\begin{itemize}
% somehow extract 1st and 3rd items from \mylist
\end{itemize}
或者
\being{itemize}
%somehow extract first n items from \mylist
\end{itemize}
可以通过说明我想要哪些项目来进行选择{1,3}
,或者更好的是,使用\label{}
和,\ref{}
这样当我添加更多项目时,我不需要更改子集中的编号。
我需要它来将出版物/参考文献/谈话的更新列表保存在一个地方,同时能够生成包含或多或少详细项目列表的各种文档。
编辑:
由于下面有很多答案,我保留了.sty
我最终编写和使用的文件的副本。它没有记录,可能会发生变化……但如果你愿意,你可以使用它。大部分内容来自 Ryan 的已接受答案,但我添加了一些内容,感谢 andrew 和 egreg(如代码中所述)。
% A package that allows the selection of items from a list based on
% the id's of the items in the list (good for CV's) based on an answer
% by Ryan Reich on TeX.SE
% http://tex.stackexchange.com/questions/4502/onlyitems-how-to-select-specific-items-from-an-item-list/32611#32611
%%%%%%%%%%%%%%%%%
\ProvidesPackage{onlyitems}
\RequirePackage{pgfkeys}
\RequirePackage{pgffor}
% A huge list of pgfkeys to be set up.
\pgfkeys{
/onlyitems/.is family, /onlyitems,
% Utility keys
utility/store tag/.store in = \itemOptions,
utility/store bool/.store in = \itemIf,
utility/process true/.style = {process/#1 = \iftrue},
utility/process false/.style = {process/#1 = \iffalse},%\fi
utility/set store bool/.style 2 args = {#1/.style = {utility/store bool = #2}},
utility/set select style/.style 2 args = {utility/set store bool = {select/#1}{#2}},
utility/verdict/.style = {},
utility/add to reset/.style = {
reset/.prefix style = {select/#1/.style = {select/.unknown}}
},
utility/current key/.estore in = \itemKey,
% Nothing in the process family is ever set, so any selection option is set to store a boolean
process/.unknown/.style = {
utility/current key = \pgfkeyscurrentname,
select/\itemKey/.style = {
utility/set store bool = {utility/verdict}{#1}
},
utility/add to reset/.expand once = \itemKey
},
% For clearing the list of selected items (to support nested {itemize})
reset/.style = {},
reset/reset/.style = {reset/.style = {reset/reset, default}},
reset/reset,
% Here are the options you can actually pass. These go to the {itemize}...
include/.style = {utility/process true/.list = {#1}},
exclude/.style = {utility/process false/.list = {#1}},
all/.style = {utility/set select style = {.unknown}{\iftrue}},
none/.style = {utility/set select style = {.unknown}{\iffalse}},%\fi
default/.style = {all},
% ...and these to the \item's
tag/.style = {utility/store tag = {[#1]}},
id/.style = {select/#1},
%yossi
.unknown/.style = {tag=\pgfkeyscurrentname} %if key is
%.unknown/.code = \onlyitemsset{utility/store tag=\pgfkeyscurrentname} %if key is
%unknown, treat it like a tag. thanks
%to Andrew Stacy: \url{http://tex.stackexchange.com/q/34376/86}
%end yossi
}
% Like tikzset, it sets a default key path
\newcommand\onlyitemsset[1]{\pgfkeys{/onlyitems, #1}}
\makeatletter
\let\itemLaTeX=\item
% Have to use a \newcount because (ugh) the LaTeX \setcounter is a global assignment
\newcount\itemsSoFar
\newcommand\onlyitemsItem[1][]{%
% The \egroup closes off any \vbox'es from the previously ignored \item.
% The conditional \bgroup cancels it out when there aren't any.
\itemIf\bgroup\fi\egroup
\let\itemIf=\iffalse%\fi
\advance\itemsSoFar by 1 %
\onlyitemsset{utility/store tag = {}, utility/verdict/.style = {}, select/\the\itemsSoFar, #1, utility/verdict}%
\itemIf
\def\next{\expandafter\itemLaTeX\itemOptions}%
\else
% The vbox is set and then discarded, effectively ignoring an entire \item.
% This inner \itemLaTeX is necessary to avoid a glitch when we
% ignore the first \item of an itemize that itself contains a nested
% \itemize. Don't ask, I don't know.
%
%the \@gobble s are so that various numerated lists still work as if
%the items are missing (i.e, no side effects). reference: http://tex.stackexchange.com/questions/34811/how-to-skip-an-item-entirely-with-no-side-effects/34821#34821
\def\next{\setbox0=\vbox\bgroup\let\stepcounter=\@gobble\let\addtocounter\@gobbletwo\itemLaTeX}%
\fi
\next
}
\makeatother
\newcommand\onlyitems[2][]{%
\let\item=\onlyitemsItem
\let\itemIf=\iftrue
\itemsSoFar = 0 %
% We have to reset here so that the selections from an outer itemize don't conflict with an inner one.
\onlyitemsset{reset, #1}%
#2%
% This closes off the last \vbox
\itemIf\bgroup\fi\egroup
}
答案1
这是另一个答案,现已完成。 它具有以下特点:
它用
pgfkeys
。它几乎与正常环境完全语法兼容
{itemize}
。唯一的变化是我必须让其\item
采用键值可选参数,因此以前唯一的可选参数现在称为tag=<something>
;这导致它<something>
成为项目的“项目符号”,就像以前一样。
编辑:根据 Yossi 的建议,我用“过滤器”命令替换了修改后的环境\onlyitems
,您可以像这样使用:\begin{environment}\onlyitems[inclusion options]\ItemListMacro...
。(如果您不使用宏,那么项目当然必须放在括号中。)您将键值选项传递给
\begin{itemize}
指定您的选择。您可以说 或include = {<list>}
,exclude = {<list>}
其中<list>
是用逗号分隔的数字和文本列表。数字可以按范围给出,a,...,b
如\foreach
。文本可以是任何您pgfkeys
认为适合键的名称。您也可以说all
或none
;默认值为all
,因此没有任何选项,其行为与itemize
通常一样。这些选项的顺序有些重要:交替使用
include
和exclude
规范将达到您的预期,除非您期望 ID 和数字得到同等对待。如果您要求同时包含和排除\item
恰好与数字和 ID 匹配的,则 ID 获胜。all
和none
选项是“弱”的:它们可以在任何地方给出,不与其他选项交互,并且被任何显式规范覆盖。每个都接受一个可选参数,该参数可以具有如上所述的
\item
键,或,其中是您可能或的东西。s 也在秘密枚举(此枚举不tag = <bullet>
id = <label>
<label>
include
exclude
\item
不是取决于包含的内容;即它仅取决于\item
TeX 文件本身有多少个 s),并且每个 s 首先通过编号与选择标准进行匹配,然后通过 id 进行匹配。您甚至可以提供多个 id,或对多个项目使用相同的 id,并产生预期的行为。排除项目是兼容的嵌套
itemize
环境。选择标准不交互;枚举不交互。任何东西都可以在里面\item
,这个东西仍然会起作用。如果不是这样的话,我六个小时前就准备好了。
下面是:
\documentclass{article}
\usepackage{pgfkeys,pgffor}
% A huge list of pgfkeys to be set up.
\pgfkeys{
/onlyitems/.is family, /onlyitems,
% Utility keys
utility/store tag/.store in = \itemOptions,
utility/store bool/.store in = \itemIf,
utility/process true/.style = {process/#1 = \iftrue},
utility/process false/.style = {process/#1 = \iffalse},%\fi
utility/set store bool/.style 2 args = {#1/.style = {utility/store bool = #2}},
utility/set select style/.style 2 args = {utility/set store bool = {select/#1}{#2}},
utility/verdict/.style = {},
utility/add to reset/.style = {
reset/.prefix style = {select/#1/.style = {select/.unknown}}
},
utility/current key/.estore in = \itemKey,
% Nothing in the process family is ever set, so any selection option is set to store a boolean
process/.unknown/.style = {
utility/current key = \pgfkeyscurrentname,
select/\itemKey/.style = {
utility/set store bool = {utility/verdict}{#1}
},
utility/add to reset/.expand once = \itemKey
},
% For clearing the list of selected items (to support nested {itemize})
reset/.style = {},
reset/reset/.style = {reset/.style = {reset/reset, default}},
reset/reset,
% Here are the options you can actually pass. These go to the {itemize}...
include/.style = {utility/process true/.list = {#1}},
exclude/.style = {utility/process false/.list = {#1}},
all/.style = {utility/set select style = {.unknown}{\iftrue}},
none/.style = {utility/set select style = {.unknown}{\iffalse}},%\fi
default/.style = {all},
% ...and these to the \item's
tag/.style = {utility/store tag = {[#1]}},
id/.style = {select/#1},
}
% Like tikzset, it sets a default key path
\newcommand\onlyitemsset[1]{\pgfkeys{/onlyitems, #1}}
\let\itemLaTeX=\item
% Have to use a \newcount because (ugh) the LaTeX \setcounter is a global assignment
\newcount\itemsSoFar
\renewcommand\item[1][]{%
% The \egroup closes off any \vbox'es from the previously ignored \item.
% The conditional \bgroup cancels it out when there aren't any.
\itemIf\bgroup\fi\egroup
\let\itemIf=\iffalse%\fi
\advance\itemsSoFar by 1 %
\onlyitemsset{utility/store tag = {}, utility/verdict/.style = {}, select/\the\itemsSoFar, #1, utility/verdict}%
\itemIf
\def\next{\expandafter\itemLaTeX\itemOptions}%
\else
% The vbox is set and then discarded, effectively ignoring an entire \item.
% This inner \itemLaTeX is necessary to avoid a glitch when we ignore the first \item of an itemize that itself contains a nested \itemize. Don't ask, I don't know.
\def\next{\setbox0=\vbox\bgroup\itemLaTeX}%
\fi
\next
}
% \let\itemizeLaTeX=\itemize
% \let\enditemizeLaTeX=\enditemize
% \renewcommand\itemize[1][]{%
% \let\itemIf=\iftrue
% \itemsSoFar = 0 %
% % We have to reset here so that the selections from an outer itemize don't conflict with an inner one.
% \onlyitemsset{reset, #1}%
% \itemizeLaTeX
% }
% \renewcommand\enditemize{%
% % This closes off the last \vbox
% \itemIf\bgroup\fi\egroup\enditemizeLaTeX
% }
\newcommand\onlyitems[2][]{%
\let\itemIf=\iftrue
\itemsSoFar = 0 %
% We have to reset here so that the selections from an outer itemize don't conflict with an inner one.
\onlyitemsset{reset, #1}%
#2%
% This closes off the last \vbox
\itemIf\bgroup\fi\egroup
}
\begin{document}
\newcommand{\mylist}{
\item First item
\item[id=banana] Second item, id=banana
\item Third item, with a nested itemize
\begin{itemize}\onlyitems
\item Nested one
\item Nested two
\item Nested three
\end{itemize}
\item[id=banana, id=papaya] Fourth item, id=banana and id=papaya
}
\noindent
All:
\begin{itemize}\onlyitems
\mylist
\end{itemize}
\noindent
Excluding 1,2,3. Note that there is no interference with the inner itemize.
\begin{itemize}\onlyitems[exclude = {1,2,3}]
\mylist
\end{itemize}
\noindent
With 2,\dots,4 excluded; again no interference.
\begin{itemize}\onlyitems[exclude = {2,...,4}]
\mylist
\end{itemize}
\noindent
With id=banana excluded:
\begin{itemize}\onlyitems[exclude = banana]
\mylist
\end{itemize}
\noindent
With 1, id=papaya excluded:
\begin{itemize}\onlyitems[exclude = {1, papaya}]
\mylist
\end{itemize}
\noindent
With none, then id=banana included, then 4 excluded. Note that the label has precedence over the number.
\begin{itemize}\onlyitems[none, include = banana, exclude = 4]
\mylist
\end{itemize}
\end{document}
非pgfkeys
部分其实很简单:键以某种方式确定布尔值\itemIf
,该布尔值依次在放置实数\item
(保存为\itemLaTeX
)和将下一个“ \item
”保存在 a 中\vbox
并将其丢弃之间切换。使用括号进行一些技巧可以实现这一点,但没有什么特别的。
不过,这个pgfkeys
区块相当复杂。其运作的基本原理是:
我希望最终让每个都
\item
处理其编号(保存在 中\itemsSoFar
)及其 id 作为键,并且这些键应该先前已设置(在 的选项中\begin{itemize}
)以适当切换\itemIf
。相应地,我需要 的编号和 id不是.unknown
设置为根据默认操作(all
或,基本上)进行处理(通过处理程序)none
。此部分包含在select
键子树中。设置这些密钥的方式
\begin{itemize}
是通过另一个.unknown
处理程序。该处理程序包含在process
密钥子树中,并且实际上是该子树的唯一成员:任何在该路径中调用的键是未知的,并受此处理程序的约束。此处理程序在select
键子树中定义键,因此process
保持空白。这使得可以重复输入相同的 ID 或数字include
和exclude
规范。但是,我不能直接改变每个 id 或数字键
\itemIf
,因为我需要它们相互覆盖,以及未知的 id 和数字。例如,如果我写入\item[id = label]
第三个项目,并且恰好包含了 3,标签未知但第二个处理,那么这不会起作用,因此实际上该项目将是排除尽管它只明确包含了指定的特性。所以我需要推迟决策,直到所有特性都处理完毕。实现此延迟的方法是让每个 id 和 number 键将其投票放入
verdict
最后执行的键中。.unknown
处理的键仍会直接修改\itemIf
,但verdict
如果已设置,则执行最后放入其中的任何内容,如果触发任何显式选择,则推翻默认值。为了支持嵌套
itemize
环境,我需要能够取消设置选择标准。唉,pgfkeys
没有办法取消设置键。幸运的是,我不需要这样做:我只需要重新定义任何先前设置的键,使其像.unknown
处理程序一样运行(实际上,这基本上是取消设置键)。因此,除了告诉新定义的 id 或数字键设置键之外verdict
,process/.unknown
处理程序还会将一段代码附加到reset
键上,告诉它撤消另一件事。然后reset
在每个 的开头调用itemize
。整个操作基本上是一个
.style
处理程序链。我从不存储值(好吧,我做了一次来扩展\pgfkeyscurrentname
),也从不直接设置.code
。每个键在执行时只会扩展为几个键,最终结果是(最终)设置\itemIf
。如果你还不知道,pgfkeys
它与其说是一个键值包,不如说是一个复杂的有限状态机规范语言,而且它恰好包含比 LaTeX 更好的 TeX 宏语言重新实现。它具有键值语法这一事实几乎是偶然的。
答案2
这是一个解决方案:
\documentclass[a4paper]{article}
\makeatletter
\newcommand{\itemlist}[2]{\@namedef{itemlist@#1}{#2}}
\newcommand{\useitemlist}[2][]{%
\if\relax\detokenize{#1}\relax
\let\do\noitemlistcheck
\else
\@for\next:=#1\do
{\expandafter\let\csname useitemlist@\next\endcsname\@empty}
\let\do\itemlistcheck
\fi
\@nameuse{itemlist@#2}%
}
\newcommand{\noitemlistcheck}[1]{\item}
\newcommand{\itemlistcheck}[1]{%
\ifcsname useitemlist@#1\endcsname
\expandafter\item
\else
\expandafter\@gobbleopt
\fi}
\newcommand\@gobbleopt[2][]{}
\makeatother
\begin{document}
\itemlist{mylist}{%
\do{a}[$\star$]{first item}
\do{b}{second item}
\do{c}{third item}
}
The following list will show all items:
\begin{itemize}
\useitemlist{mylist}
\end{itemize}
The following list will show only items "a" and "c"
\begin{itemize}
\useitemlist[a,c]{mylist}
\end{itemize}
\end{document}
你可以使用以下方式定义项目列表
\itemlist{<name>}{%
\do{<label-1>}{<item text>}
\do{<label-2>}{<item text>}
...
\do{<label-n>}{<item text>}
}
然后说\useitemlist{<name>}
打印整个列表或\useitemlist[<list of labels>]{<name>}
仅打印所需的项目,并通过作为第一个参数给出的符号名称调用它们。
在列表中我已经证明了\do
,在键之后,可以采用一个可选参数,该参数将成为的参数\item
。
答案3
如果你真的如果你想,你可以滥用 biblatex+biber 来创建任何列表。以下非常简单。我想可以为 biber 编写一个配置文件,让事情看起来更自然(如果需要,还可以进行排序)。
无论如何,biblatex
定义custom[a-f]
可以(除其他外)包含user[a-f]
字段的条目类型。因此可以创建.bib
这样的文件。
@customa{entry-one,
usera = {one}
}
@customa{entry-two,
usera = {two \textit{bla} bla\par
bla}
}
@customa{entry-three,
usera = {three}
}
@customa{entry-four,
usera = {four}
}
@customa{entry-five,
usera = {five}
}
@customa{entry-six,
usera = {six}
}
然后只剩下biblatex
如何格式化事物并使用\nocite
告诉它要打印列表的哪一部分:
\documentclass{article}
\usepackage[backend=biber]{biblatex}
% Make `\printbibliography[env=list]` print a simple itemize list
\defbibenvironment{list}
{\begin{itemize}}
{\end{itemize}}
{\item}
% Define how `customa` entries are displayed (simply pass through the usera field).
\DeclareBibliographyDriver{customa}
{\printfield{usera}}
\addbibresource{test.bib}
\begin{document}
\nocite{entry-one,entry-six,entry-four,entry-two}
\printbibliography[env=list,heading=none]
\end{document}
跑步
pdflatex test
biber test
pdflatex test
然后产生
一个警告:biber 似乎从usera
字段中删除了换行符。这意味着段落必须明确标记\par
,并且不能有任何注释。
如果您希望在一个文档中包含多个列表,refsection
可以使用环境(参见 biblatex 手册)。如果您还想要一个普通的书目,您可以使用 biblatex 的工具来处理几个单独的书目资源。
答案4
这是我的方法。请注意,它需要 eTeX 和电子工具箱包裹。
\documentclass{article}
\usepackage{etoolbox}
\makeatletter
\newenvironment{DefConditionalList}[1]
{\def\citem##1##2{%
\ifcsname ConditionalList@#1@item@##1\endcsname
\PackageError{ConditionalList}%
{List item `##1' in conditional list `#1' already defined}%
{The list item `##1' was already defined via
`\noexpand\citem{##1}{...}' in the conditional list `#1'.}%
\else
\csgdef{ConditionalList@#1@item@##1}{\item ##2}%
\csgdef{ConditionalList@#1@item@##1 }{\item ##2}%
\fi}}
{}
\newcommand*{\ConditionalList}[3][]{\begingroup
\ifblank{#3}{%
#1%
}{%
\def\do##1{%
\ifcsname ConditionalList@#2@item@##1\endcsname
\csname ConditionalList@#2@item@##1\endcsname
\else
\PackageError{ConditionalList}%
{Unknown list item `##1' in conditional list `#2'}%
{The list item `##1' was not defined via
`\noexpand\citem{##1}{...}' in the conditional list
`#2'.}%
\fi
}%
\docsvlist{#3}%
\let\ConditionalList@DisplayItem\ConditionalList@DisplayItem@error
}%
\endgroup}
\makeatother
\begin{DefConditionalList}{MyList}
\citem{first}{Alpha.}
\citem{second}{Beta.}
\citem{third}{Gamma.}
\citem{fourth}{Delta.}
\end{DefConditionalList}
\edef\resume{first, fourth}
\begin{document}
\begin{itemize}
\ConditionalList[\item Nothing specified!]{MyList}{first, third}
\ConditionalList[\item Nothing specified!]{MyList}{ fourth
, third
, fourth }
\end{itemize}
\begin{enumerate}
\ConditionalList[\item Nothing specified!]{MyList}{}
% Eror: \ConditionalList[\item Nothing specified!]{MyList}{nonexistent}
\end{enumerate}
\end{document}
这将产生以下输出:
- Α。
- 伽玛。
- 三角洲。
- 伽玛。
- 三角洲。
- 没有指定!
这个想法是为环境中指定的每个项目定义包私有命令DefConditionalList
。然后,\ConditionalList
可以循环遍历请求的名称,检查是否定义了相应的命令,如果是,则插入它们。顺序和重复无关紧要,如果指定了不存在的键,则会出错,如果没有指定任何项目,则可以指定要执行的操作(默认情况下,不会插入任何内容)。不允许在末尾定义带有空格的条件列表项(尽管它可能无论如何都有效……),因此可以在逗号周围提供任意空格。