
Я создал макрос, \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 , and a1 , and b1 , and 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
файле.