
Criei uma macro \joinlist
que adiciona vírgulas ou outros delimitadores entre os elementos de uma etoolbox
lista interna. Possui tratamento especial para listas de apenas dois elementos ("a e b"). Ele também sabe colocar um delimitador diferente entre os dois últimos itens de listas mais longas ("a, b e c"). Aqui está a definição, criada por mim:
% 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}}
Salve o texto acima como join-list.sty
e considere o seguinte pequeno exemplo mostrando um problema:
\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
funciona bem para criar conteúdo do corpo do documento. Vemos "a", depois "a e b" e depois "a, b e c" conforme esperado.
Infelizmente, usar isso \hypersetup{pdfauthor=...}
não funciona como esperado. Em vez disso, ele define os metadados do autor PDF do documento como "0 1110 1 e a1 e b1 e c". É evidente que algo correu mal na expansão/avaliação da \joinlist
macro neste contexto.
O que estou fazendo de errado aqui? Como posso consertar isso? Existem outras maneiras de \joinlist
melhorar minha macro? Já existe uma macro semelhante em algum pacote amplamente disponível? (Eu procurei, mas não encontrei nenhum.)
Responder1
O problema é que \joinlist
não é "expansível": ele deve realizar atribuições como \advance\@join@currentnum 1
as que não podem ser feitas no valor de pdfauthor=
.
Com expl3
(a camada de programação não mais experimental para LaTeX3), existe o que você precisa.
\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}
Existem algumas alterações em relação à sua configuração. As listas têm um nome, em vez de serem chamadas por uma macro; um novo pode ser criado por \newlist
. Além disso, a ordem dos argumentos \joinlist
é diferente:
- o nome da lista;
- o que deve ficar entre os elementos no caso de dois elementos;
- o que deve ficar entre os elementos quando houver mais de dois (exceto os dois últimos);
- o que deveria acontecer entredurardois elementos quando há mais de dois.
Caso a lista esteja vazia nada será produzido; com apenas um elemento, esse será produzido.
Esta versão \joinlist
é expansível e não há problema em atribuí-la como valor a pdfauthor=
.
Você pode usar o código no preâmbulo como um substituto para o seu no .sty
arquivo.