onlyitems? 如何从项目列表中选择特定项目

onlyitems? 如何从项目列表中选择特定项目

我有以下设置,我有一个包含一些定义的文件,其中一个是项目列表:

\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

这是另一个答案,现已完成。 它具有以下特点:

  1. 它用pgfkeys

  2. 它几乎与正常环境完全语法兼容{itemize}。唯一的变化是我必须让其\item采用键值可选参数,因此以前唯一的可选参数现在称为tag=<something>;这导致它<something>成为项目的“项目符号”,就像以前一样。

    编辑:根据 Yossi 的建议,我用“过滤器”命令替换了修改后的环境\onlyitems,您可以像这样使用:\begin{environment}\onlyitems[inclusion options]\ItemListMacro...。(如果您不使用宏,那么项目当然必须放在括号中。)

  3. 您将键值选项传递给\begin{itemize}指定您的选择。您可以说 或include = {<list>}exclude = {<list>}其中<list>是用逗号分隔的数字和文本列表。数字可以按范围给出,a,...,b\foreach。文本可以是任何您pgfkeys认为适合键的名称。您也可以说allnone;默认值为all,因此没有任何选项,其行为与itemize通常一样。

  4. 这些选项的顺序有些重要:交替使用includeexclude规范将达到您的预期,除非您期望 ID 和数字得到同等对待。如果您要求同时包含和排除\item恰好与数字和 ID 匹配的,则 ID 获胜。allnone选项是“弱”的:它们可以在任何地方给出,不与其他选项交互,并且被任何显式规范覆盖。

  5. 每个都接受一个可选参数,该参数可以具有如上所述的\item键,或,其中是您可能或的东西。s 也在秘密枚举(此枚举不tag = <bullet>id = <label><label>includeexclude\item不是取决于包含的内容;即它仅取决于\itemTeX 文件本身有多少个 s),并且每个 s 首先通过编号与选择标准进行匹配,然后通过 id 进行匹配。您甚至可以提供多个 id,或对多个项目使用相同的 id,并产生预期的行为。

  6. 排除项目是兼容的嵌套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 或数字includeexclude规范。

  • 但是,我不能直接改变每个 id 或数字键\itemIf,因为我需要它们相互覆盖,以及未知的 id 和数字。例如,如果我写入\item[id = label]第三个项目,并且恰好包含了 3,标签未知但第二个处理,那么这不会起作用,因此实际上该项目将是排除尽管它只明确包含了指定的特性。所以我需要推迟决策,直到所有特性都处理完毕。

  • 实现此延迟的方法是让每个 id 和 number 键将其投票放入verdict最后执行的键中。.unknown处理的键仍会直接修改\itemIf,但verdict如果已设置,则执行最后放入其中的任何内容,如果触发任何显式选择,则推翻默认值。

  • 为了支持嵌套itemize环境,我需要能够取消设置选择标准。唉,pgfkeys没有办法取消设置键。幸运的是,我不需要这样做:我只需要重新定义任何先前设置的键,使其像.unknown处理程序一样运行(实际上,这基本上是取消设置键)。因此,除了告诉新定义的 id 或数字键设置键之外verdictprocess/.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}

这将产生以下输出:

  1. Α。
  2. 伽玛。
  3. 三角洲。
  4. 伽玛。
  5. 三角洲。
  • 没有指定!

这个想法是为环境中指定的每个项目定义包私有命令DefConditionalList。然后,\ConditionalList可以循环遍历请求的名称,检查是否定义了相应的命令,如果是,则插入它们。顺序和重复无关紧要,如果指定了不存在的键,则会出错,如果没有指定任何项目,则可以指定要执行的操作(默认情况下,不会插入任何内容)。不允许在末尾定义带有空格的条件列表项(尽管它可能无论如何都有效……),因此可以在逗号周围提供任意空格。

相关内容