我正在尝试tabular
用 etoolbox 列表填充环境:
\documentclass{article}
\usepackage{etoolbox}
\listadd\mylist{Ana}%
\listadd\mylist{Bob}%
\listadd\mylist{Cole}%
\newcommand*{\pco}{%
\renewcommand*{\do}[1]{Person: & ##1 \\}
\dolistloop{\mylist}
}
\begin{document}
\begin{tabular}{r | l}
\pco
\end{tabular}
\end{document}
我的意思是生成如下表格:
Person: | Ana
Person: | Bob
Person: | Cole
相反,我得到的是:
Person: | Ana
BobCole |
我很难弄清楚这一点。我的第一个想法是,该\do
命令被表理解为单个标记(即{Person: & ##1 \\}
)。但在第一个实例中情况并非如此:列表中的第一个项目成功分成两列。但是,以下项目并非如此;事实上,\do
似乎完全被忽略了,并且每个列表项的值只是被打印出来。
我错过了什么?
答案1
改用\forlistloop
:
\documentclass{article}
\usepackage{etoolbox}
\newcommand{\pcodo}[1]{Person: & #1 \\}
\listadd\mylist{Ana}
\listadd\mylist{Bob}
\listadd\mylist{Cole}
\newcommand*{\pco}{\forlistloop{\pcodo}{\mylist}}
\begin{document}
\begin{tabular}{r | l}
\pco
\end{tabular}
\end{document}
不过,还有更好的方法。这里有一个与 类似的实现\xintFor
,但允许逐步构建列表。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
% initialize a list
\NewDocumentCommand{\newlist}{m}
{ % #1=list name
\seq_new:c { g_wasabi_list_#1_seq }
}
% populate a list one item at a time
\NewDocumentCommand{\addtolist}{mm}
{
\seq_gput_right:cn { g_wasabi_list_#1_seq } { #2 }
}
% use a list; "pre" and "post" are code executed before and after the loop
\NewDocumentCommand{\uselist}{mO{}mO{}}
{ % #1=list name, #2=pre, #3=template, #4=post
\cs_gset:Nn \__wasabi_list_temp:n { #3 }
#2
\seq_map_function:cN { g_wasabi_list_#1_seq } \__wasabi_list_temp:n
#4
}
\ExplSyntaxOff
\newlist{my}
%% these commands can go anywhere, provided they are before
%% using the list
\addtolist{my}{Ana}
\addtolist{my}{Bob}
\addtolist{my}{Cole}
\begin{document}
\uselist{my}
[\begin{tabular}{r|l}]
{Person & #1 \\}
[\end{tabular}]
\bigskip
\uselist{my}{#1, }
\end{document}
列表有多种使用方式,并具有不同的模板。
答案2
使用tabular
s,通常更容易建立(即连续连接)一个标记列表,然后在过程结束时将其转储到输入流中。
\documentclass{article}
\usepackage{etoolbox}
\listadd\mylist{Ana}%
\listadd\mylist{Bob}%
\listadd\mylist{Cole}%
\newtoks\tabtoks
\newcommand*{\pco}{%
\tabtoks{}%
\newcommand*{\ZaZa}[1]{\tabtoks\expandafter{\the\tabtoks Person: & ##1 \\}}%
\forlistloop\ZaZa{\mylist}%
\the\tabtoks%
}
\begin{document}
\begin{tabular}{r | l}
\pco
\end{tabular}
\end{document}
但是,感谢 jfbu 指出,这个问题可以更简单地解决。OP 代码的问题在于,&
列分隔符在 下创建了不同的范围\halign
,因此必须在该约束下工作,以使其他tabular
单元格全局可用。例如,在 OP 的原始代码中,只需更改\renewcommand*{\do}[1]{Person: & ##1 \\}
为\gdef\do##1{Person: & ##1 \\}
即可使代码产生正确的输出。但是,感谢 egreg 指出\do
全局重新定义是不可接受的,并指出\forlistloop
是最佳替代方案。
日本保卫工会联合会提出以下决议:
\newcommand*{\pco}{%
\gdef\ZaZa##1{Person: & ##1 \\}%
\forlistloop\ZaZa{\mylist}%
}
产生可以工作的代碼。
\documentclass{article}
\usepackage{etoolbox}
\listadd\mylist{Ana}%
\listadd\mylist{Bob}%
\listadd\mylist{Cole}%
\newcommand*{\pco}{%
\gdef\ZaZa##1{Person: & ##1 \\}%
\forlistloop\ZaZa{\mylist}%
}
\begin{document}
\begin{tabular}{r | l}
\pco
\end{tabular}
\end{document}
答案3
除了其他两个优秀的答案之外,我还可以提出这个不需要定义额外宏的答案:
\documentclass{article}
\usepackage{xinttools}
\newcommand\mylist{Ana, Bob, Cole}
\begin{document}
\begin{tabular}{r | l}
\xintFor #1 in \mylist \do{Person: & #1 \\}
\end{tabular}
% or \xintFor #1 in {Ana, Bob, Cole}
% or \xintFor* #1 in {{Ana}{Bob}{Cole}}
\end{document}