是否存在一个环境(或 itemize 的宏)可以在每次生成 pdf 时随机打乱所有项目?
答案1
值得一提的是,ConTeXt 开箱即用地支持此功能。只需将random
键添加到即可\startitemize
。(在 ConTeXt 中,\startitemize[n]
相当于\begin{enumerate}
LaTeX 的)。
\starttext
\startitemize[n,random]
\startitem One \stopitem
\startitem Two \stopitem
\startitem Three \stopitem
\startitem Four \stopitem
\startitem Five \stopitem
\startitem Six \stopitem
\stopitemize
\stoptext
这使:
随机种子存储在 tuc 文件中,这样每次编译文件时都会得到相同的输出(否则,多次编译会导致无限循环,并且可能无法获得所有正确的引用)。因此,您需要删除 tuc 文件来更改随机种子,从而获得不同的输出。
答案2
为了生成随机排序的列表,我定义了一个新环境,randomList
,其使用方式与enumerate
环境及其朋友几乎完全相同。该代码可以与enumerate
、itemize
和description
环境一起使用,事实上,还可以与任何类似的“列表环境”一起使用,例如使用枚举项包裹。
代码\par
要求每项之后都有一个空行 (a ),包括列表中的最后一项。例如,以下代码将随机排列三个列表项:
\begin{randomList}
\item First item
\item Second item
\item Third item
\end{randomList}
列表项可以包含多行文本。段落需要用括号括起来。
基本思想是重新定义\item
,以便它“吸收”从 到下\item
一个 的所有内容\par
,然后将其保存为名为 的宏\randomListItem@k
,表示项目编号k
。在“读入”列表时,1,2,...,n
会构建列表索引的随机排列。最后,在环境结束时,使用\forlistloop
来自电子工具箱。
似乎没有一种简单/已知的方法/程序包来生成 的随机排列1,2,...,n
,事实上,下面的大部分代码都用于执行此操作。随机排列是通过递归插入 的n
随机排列来构建的1,2,...,n-1
。n
插入后,它会被随机放入现有长度为 的序列中,n-1
使用\pgfmathparse{random(1,n)}
,来自数学\randomListItem@k
包。在下面的代码中,使用宏构建随机排列,同时构建项目\randomlyInsertInList
。此代码可用于构建任意(逗号分隔)列表的随机排列,但我尚未对此进行测试。(另请注意,排列列表是电子工具箱列表而不是 CSV 列表。
以下是 MWE,它给出了环境所需代码的定义randomList
以及一些示例来说明如何使用它。如前所述,为了使代码正常工作,每个项目后面都必须有一个空行。
\documentclass{article}
\usepackage{etoolbox}
\usepackage{pgfkeys}
\usepackage{pgfmath}
% code for generating a random permutation
\newcounter{randomListLength}% current length of our random list
\newcounter{randomListPosition}% current list index
\newcounter{newRandomListElementPosition}% position to insert new element
% insert #1 into the next position of \newRandomList unless the position
% index \randomListPosition is equal to \newRandomListElementPosition in
% which case the \newRandomListElement is added first
\newcommand\randomlyInsertElement[1]{%
\stepcounter{randomListPosition}%
\ifnum\value{randomListPosition}=\value{newRandomListElementPosition}%
\listxadd\newRandomList{\newRandomListElement}%
\fi%
\listxadd\newRandomList{#1}%
}
% \randomlyInsertInList{list name}{new list length}{new element}
\newcommand\randomlyInsertInList[3]{%
\pgfmathparse{random(1,#2)}%
\setcounter{newRandomListElementPosition}{\pgfmathresult}%
\ifnum\value{newRandomListElementPosition}=#2\relax%
\listcsxadd{#1}{#3}%
\else%
\def\newRandomList{}% start with an empty list
\def\newRandomListElement{#3}% and the element that we need to add
\setcounter{randomListPosition}{0}% starting from position 0
\xdef\currentList{\csuse{#1}}
\forlistloop\randomlyInsertElement\currentList%
\csxdef{#1}{\newRandomList}%
\fi%
}
% define some pgfkeys to allow key-value arguments
\pgfkeys{/randomList/.is family, /randomList,
environment/.code = {\global\letcs\beginRandomListEnvironment{#1}
\global\letcs\endRandomListEnvironment{end#1}
},
enumerate/.style = {environment=enumerate},
itemize/.style = {environment=itemize},
description/.style = {environment=description},
seed/.code = {\pgfmathsetseed{#1}}
}
\pgfkeys{/randomList, enumerate}% enumerate is the default
% finally, the code to construct the randomly permuted list
\makeatletter
\newcounter{randomListCounter}% for constructing \randomListItem@<k>'s
% \useRandomItem{k} prints item number k
\newcommand\useRandomItem[1]{\csname randomListItem@#1\endcsname}
% \setRandomItem{k} saves item number k for future use
% and builds a random permutation at the same time
\def\setRandomItem#1\par{\stepcounter{randomListCounter}%
\expandafter\protected@xdef\csname randomListItem@\therandomListCounter\endcsname{\noexpand\item#1}%
\randomlyInsertInList{randomlyOrderedList}{\therandomListCounter}{\therandomListCounter}%
}%
\let\realitem=\item
\makeatother
\newenvironment{randomList}[1][]{% optional argument -> pgfkeys
\pgfkeys{/randomList, #1}% process optional arguments
\setcounter{randomListLength}{0}% initialise length of random list
\def\randomlyOrderedList{}% initialise the random list of items
% Nthing is printed in the main environment. Instead, \item is
% used to slurp the "contents" of the item into randomListItem@<counter>
\let\item\setRandomItem%
}
{% now construct the list environment by looping over the randomly ordered list
\let\item\realitem
\setcounter{randomListCounter}{0}
\beginRandomListEnvironment\relax
\forlistloop\useRandomItem\randomlyOrderedList
\endRandomListEnvironment
}
% test compatibility with enumitem
\usepackage{enumitem}
\newlist{Testlist}{enumerate}{1} %
\setlist[Testlist]{label*=\alph*.}
\setlist{nosep}\parindent=0pt% for more compact output
\begin{document}
\textbf{Enumerate example:}
\begin{randomList}
\item First item
\item Second item
\item Third item
\end{randomList}
\textbf{Itemise example:}
\begin{randomList}[itemize]
\item First item
\item Second item
\item Third item
\end{randomList}
\textbf{Description example}
\begin{randomList}[description]
\item[D1] First item
\item[D2] Second item
\item[D3] Third item
\end{randomList}
\textbf{Testlist example with seed}
\begin{randomList}[environment=Testlist, seed=4]
\item First item
\item Second item
\item Third item
\end{randomList}
\end{document}
这(目前)产生以下输出(但由于随机排序,可能会根据代码运行的时间而发生变化):
默认情况下,randomList
随机排列环境中的项目enumerate
。如 MWE 中所示,可选参数itemize
或description
将randomList
使用这两个环境(如果给出了两个或更多个环境,则最后一个获胜)。还有另一个可选参数 用于seed=<random seed>
设置 使用的随机种子\pgfmathparse{random(*)}
。这只需调用\pgfmathsetseed
。
如最后一个例子所示,该代码与枚举项\newlist
包。如果使用from定义了新的列表环境,enumitem
则randomList
如果您使用可选参数 ,则将使用此环境environment=<listname>
。(在我的实际用例中,我想使用自定义enumitem
列表环境,因此这对我来说很重要。)最终,列表元素打印在枚举、逐项列出、描述... 环境中,因此适用于这些环境的任何东西都应该适用于randomList
。
的可选参数randomList
使用以下方式处理鍵盤. 可以使用 直接访问它们\pgfkeys{/randomList, ...}
。
最后的评论:我认为这会很慢,但我基本上使用这个代码来生成 20 个问题列表的三个随机排序版本,以及一些其他内容,而且它真的很快!
答案3
今天(对于楼主来说已经很晚了)我发现randomlist
包,其中包括提供\RandomItemizeList
和\RandomEnumerateList
命令。这些命令会产生它们所说的内容,即带有或不带有编号的项目的随机列表。
举个小例子:
\documentclass{article}
\usepackage{randomlist}
\begin{document}
This is a random list of items:
\RandomItemizeList{First item}{Second item}{Third item}{Fourth item}
and this is an enumerated random list of items:
\RandomEnumerateList{First item}{Second item}{Third item}{Fourth item}
\end{document}
第一次编译后,它产生:
第二次编译得到:
等等。
答案4
由于的通常实现\begin{list} \item ... \item ... \item ... \end{list}
不会读取或保存项目的实际文本,如果您想要相同的界面,则需要进行一些超出我能力范围的黑客攻击来保存然后排列列表项。
我问了一个类似问题在几年前的 pgf-users 邮件列表中。这是 Mark Wibrow 对随机化 PGF 列表(声明为 )的回答\pgfmathdeclarelist
。从那时起,我就把这个片段放到了我的考试文件中。
\documentclass{article}
\usepackage{tikz}
\begin{document}
\makeatletter
\def\pgfmathdeclarelist#1#2{%
\def\pgfmath@list@name{#1}%
\c@pgfmath@counta=0%
\pgfmath@declarelistlist#2{\pgfmath@stop}%
}%
\def\pgfmath@declarelistlist#1{%
\ifx#1\pgfmath@stop%
\expandafter\edef\csname pgfmath@list@\pgfmath@list@name
@length\endcsname{\the\c@pgfmath@counta}%
\else%
\advance\c@pgfmath@counta by1\relax%
\pgfutil@namedef{pgfmath@list@\pgfmath@list@name @\the\c@pgfmath@counta}{#1}%
\expandafter\pgfmath@declarelistlist%
\fi%
}
\def\pgfmathgetlistitem#1#2#3{\expandafter\let\expandafter#1\expandafter=\csname
pgfmath@list@#2@#3\endcsname}
\def\pgfmathsetlistitem#1#2#3{%
\pgfutil@namedef{pgfmath@list@#1@#2}{#3}%
}
\def\pgfmathgetlistlength#1#2{%
\expandafter\let\expandafter#1\expandafter=\csname
pgfmath@list@#2@length\endcsname%
}
\def\pgfmathknuthshuffle#1{%
\pgfmathgetlistlength\pgfmath@len{#1}%
\pgfmathloop%
\ifnum\pgfmathcounter>\pgfmath@len%
\else%
\pgfmathrandominteger\pgfmath@temp{1}{\pgfmath@len}%
\pgfmathgetlistitem\pgfmath@@temp{#1}{\pgfmathcounter}%
\pgfmathgetlistitem\pgfmath@@@temp{#1}{\pgfmath@temp}%
\def\pgfmath@marshal{\pgfmathsetlistitem{#1}}%
\expandafter\pgfmath@marshal\expandafter{\expandafter\pgfmath@temp\expandafter}\expandafter{\pgfmath@@temp}%
\expandafter\pgfmath@marshal\expandafter{\expandafter\pgfmathcounter\expandafter}\expandafter{\pgfmath@@@temp}%
\repeatpgfmathloop%
}
\makeatother
\pgfmathdeclarelist{mylist}{{A}{B}{C}{D}{E}{F}{G}{H}{I}{J}{K}{L}{M}}
\pgfmathgetlistlength{\l}{mylist}
\begin{tikzpicture}[every node/.style={circle, draw}]
\foreach \i in {1,...,\l}{
\pgfmathgetlistitem{\x}{mylist}{\i}
\node at (0,-\i) (\x-1) {\x};
}
\pgfmathknuthshuffle{mylist}
\foreach \i in {1,...,\l}{
\pgfmathgetlistitem{\x}{mylist}{\i}
\node at (7.5,-\i) (\x-2) {\x};
}
\foreach \i in {1,...,\l}{
\pgfmathgetlistitem{\x}{mylist}{\i}
\draw [->] (\x-1) -- (\x-2);
}
\end{tikzpicture}
\end{document}