我创建了一个宏,\joinlist
它在内部列表的元素之间添加逗号或其他分隔符etoolbox
。它对只有两个元素的列表(“a 和 b”)有特殊处理。它还知道如何在较长列表的最后两项(“a、b 和 c”)之间放置不同的分隔符。这是我创建的定义:
% typical use: \joinlist{\listmacro}{, }{, and }{ and }
\RequirePackage{etoolbox}
\newcommand{\@join@ignore}[1]{} % used to ignore current list element when counting
\newcount\@join@listlength % used to count length of list
\newcount\@join@currentnum % used to track current list element number
\newcommand{\joinlist}[4]{%
%
% count list elements
\@join@listlength 0 %
\forlistloop{\advance\@join@listlength 1\relax\@join@ignore}{#1}%
%
% now join list elements, tracking current element number
\@join@currentnum 0 %
\forlistloop{%
\advance\@join@currentnum 1 %
\ifnumequal{\the\@join@currentnum}{1}%
{}% first
{% not first
\ifnumequal{\the\@join@currentnum}{\the\@join@listlength}%
{% last
\ifnumequal{\the\@join@listlength}{2}%
{#4}% last of exactly two
{#3}% last of more than two
}%
{#2}% neither first nor last
}%
}%
{#1}}
将以上内容保存为join-list.sty
,然后考虑以下显示问题的小示例:
\documentclass{article}
\usepackage{hyperref}
\usepackage{join-list}
\begin{document}
\newcommand{\people}[0]{}
\listadd{\people}{a}
\joinlist{\people}{, }{, and }{ and } % a
\listadd{\people}{b}
\joinlist{\people}{, }{, and }{ and } % a and b
\listadd{\people}{c}
\joinlist{\people}{, }{, and }{ and } % a, b, and c
\hypersetup{pdfauthor=\joinlist{\people}{, }{, and }{ and }}
\end{document}
\joinlist
非常适合创建文档正文内容。我们按预期看到“a”,然后是“a 和 b”,然后是“a、b 和 c”。
不幸的是,使用这个\hypersetup{pdfauthor=...}
宏并没有像预期的那样起作用。相反,它将文档的 PDF 作者元数据设置为“0 1110 1 ,和 a1 ,和 b1 ,和 c”。显然,\joinlist
在这个上下文中扩展/评估宏时出现了严重错误。
我在这里做错了什么?我该如何解决这个问题?还有其他方法\joinlist
可以改进我的宏吗?一些广泛使用的软件包中是否已经存在类似的宏?(我搜索过但没有找到。)
答案1
问题在于不是“可扩展的”:它必须执行在值中无法完成的\joinlist
分配,例如。\advance\@join@currentnum 1
pdfauthor=
有了expl3
(不再是实验性的 LaTeX3 编程层),您所需要的就有了。
\documentclass{article}
\usepackage{hyperref}
\RequirePackage{xparse}
\ExplSyntaxOn
\DeclareExpandableDocumentCommand{\joinlist}{ m m m m }
{
\seq_use:cnnn { g_liblit_list_#1_seq } { #2 } { #3 } { #4 }
}
\cs_generate_variant:Nn \seq_use:Nnnn { c }
\NewDocumentCommand{\newlist}{ m }
{
\seq_new:c { g_liblit_list_#1_seq }
}
\NewDocumentCommand{\listadd}{ m m }
{
\seq_gput_right:cn { g_liblit_list_#1_seq } { #2 }
}
\ExplSyntaxOff
\begin{document}
\newlist{people}
\listadd{people}{a}
\joinlist{people}{ and }{, }{, and } % a
\listadd{people}{b}
\joinlist{people}{ and }{, }{, and } % a and b
\listadd{people}{c}
\joinlist{people}{ and }{, }{, and } % a, b, and c
\hypersetup{pdfauthor=\joinlist{people}{ and }{, }{, and }}
\end{document}
您的设置有一些变化。列表有一个名称,而不是由宏调用;可以通过创建一个新的列表\newlist
。此外,参数的顺序也\joinlist
不同:
- 列表名称;
- 如果有两个元素,元素之间应该有什么?
- 当有两个以上元素时(最后两个除外),元素之间应该做什么;
- 应该在最后的当有两个以上元素时,为两个元素。
如果列表为空,则不会生成任何内容;如果只有一个元素,则会生成该元素。
此版本的\joinlist
是可扩展的,并且将其作为 的值赋予 没有任何问题pdfauthor=
。
您可以使用序言中的代码作为文件中您的代码的替换.sty
。