使用复杂的列表处理宏设置 pdfauthor

使用复杂的列表处理宏设置 pdfauthor

我创建了一个宏,\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 1pdfauthor=

有了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不同:

  1. 列表名称;
  2. 如果有两个元素,元素之间应该有什么?
  3. 当有两个以上元素时(最后两个除外),元素之间应该做什么;
  4. 应该在最后的当有两个以上元素时,为两个元素。

如果列表为空,则不会生成任何内容;如果只有一个元素,则会生成该元素。

此版本的\joinlist是可扩展的,并且将其作为 的值赋予 没有任何问题pdfauthor=

您可以使用序言中的代码作为文件中您的代码的替换.sty

在此处输入图片描述

相关内容