使用任意数量的参数包装命令

使用任意数量的参数包装命令

我经常使用 LaTeX,有时会重复使用文本片段。但是,不同的出版商使用不同的软件包和后端进行引用。因此,我发现将特定命令包装成更通用的命令的想法很方便。但我从未解决过向命令提供任意数量的来源的问题。我遇到了关于自动生成序列的非常好的帖子,但我遇到了困难,因为我需要在发出命令(或)之前生成像{,test1,35,test2,,test3}=>这样的序列。{}{test1}{35}{test2}{test3}\autocites...\parencitespost 似乎更好地解决了这个问题,但它再次只生成一系列命令,而不是单个命令的输入序列。

\documentclass{article}

\usepackage[backend = biber]{biblatex}
\addbibresource{sample.bib} 

% wrapper command with 4 entries (for 2 sources)
\newcommand{\myCitation}[4]{\autocites[#1]{#2}[#3]{#4}}

\begin{document}
    single citation \cite{test1}\newline
    standard command:   \autocites{test1}[35]{test2}\newline
    wrapped command:    \myCitation{}{test1}{35}{test2}
    desired command:    \myCitation{}{test1}{35}{test2}{42}{test3} % simply expanding => page number "42" is considered as (undefined) reference
\end{document}

编译的LaTeX-PDF

MVE 假设 bib 文件sample.bib包含以下内容:

@article{test1,
    title={This is a nice title},
    author={TestEntry A.},
    journal={Journal of advanced References}, 
    year={2020}
}
@article{test2,
    title={Best article ever!},
    author={TestEntry B.},
    journal={Conference on words}, 
    year={2019}
}
@article{test3,
    title={Bla bla blubb},
    author={TestEntry C.},
    journal={Conference on words}, 
    year={2019}
}

目的是将{a}{b}{c}{d}...任意长度(但为 2 的倍数)的序列\autocite以 形式传递给命令[a]{b}[c]{d}...

也许这是在发出命令之前构建命令的错误方法。有什么想法吗?

答案1

但我从来没有解决过向命令提供任意数量的源的问题。

如果你所说的“任意数量的来源”是指“任意数量的无界参数”,那么答案的一个方面可能是尾递归

在 TeX 中,所谓的尾递归宏(或尾递归宏机制)通过使用修改后的/不同的参数调用自身来终止,除非参数满足终止递归的某些条件。

例如,以下宏\GreetingsToPeopleLoop按如下方式处理非分隔参数:

  • 如果参数的第一个标记的含义不等于-primitive 的含义\relax,则传递参数,加上前缀Hello,和尾随!\par,然后再次调用\GreetingsToPeopleLoop
  • 如果参数的第一个标记的含义确实等于-primitive的含义\relax,则不执行任何操作。

以这种方式递归地处理一系列未限定的参数:

\documentclass{article}
\newcommand\GreetingsToPeopleLoop[1]{%
  \ifx\relax#1\else Hello, #1!\par\expandafter\GreetingsToPeopleLoop\fi
}%
\begin{document}
\GreetingsToPeopleLoop{Lisa}{Bart}{Maggie}{Marge}%
                      {Homer}{Abe}{Ned}{Milhouse}%
                      {Montgomery}{Seymour}{Moe}%
                      {Barney}{Lenny}{Carl}{Ralph}%
                      {Crusty}{\relax}%
\end{document}

在此处输入图片描述

\expandafter\fi在处理 之前用于处理\GreetingsToPeopleLoop\fi是可扩展的,并且在处理过程中从标记流中删除,而不会返回替换标记。 因此,在收集 的\fi未限定参数时,它不再“妨碍” 。\GreetingsToPeopleLoop
同样重要的是:由于\fi已经处理过,因此将其从输入堆栈中删除。
使用尾递归宏时,始终确保在调用尾递归宏进行下一次迭代之前已处理完所有\else和。\fi否则,属于未处理\else/\fi分支且要丢弃的标记可能会在输入堆栈中累积,直到出现! TeX capacity exceeded, sorry错误。

然而,通过这种方法,恶意用户可以轻易地引发关于 的错误消息! Extra \else.以及关于 的其他错误消息! Extra \fi.

\GreetingsToPeopleLoop{Lisa}{\relax\fi Sideshow Bob}{\relax}%

当然你可以这样做:

\documentclass{article}
\newcommand\GreetingsToPeople[1]{%
  \GreetingsToPeopleLoop#1{\relax}%
}%
\newcommand\GreetingsToPeopleLoop[1]{%
  \ifx\relax#1\else Hello, #1!\par\expandafter\GreetingsToPeopleLoop\fi
}%
\begin{document}
\GreetingsToPeople{%
  {Lisa}{Bart}{Maggie}{Marge}{Homer}%
  {Abe}{Ned}{Milhouse}{Montgomery}{Seymour}%
  {Moe}{Barney}{Lenny}{Carl}{Ralph}{Crusty}%
}%
\end{document}

在此处输入图片描述


这是一个尾部递归处理未限定参数列表的非常简单的例子。

您还可以应用尾递归来执行算法,其中宏参数充当由尾递归宏本身修改的变量。
在这种情况下,交换宏参数或​​使用“辅助宏”将宏参数(嵌套在括号中)放在其他宏参数后面是很方便的。

通过以下示例,宏机制\increment{<number>}{<amount of increments>}将给定的整数增加几次,并且每次打印结果:

\documentclass{article}
\newcommand\PassFirstToSecond[2]{#2{#1}}
\newcommand\GobbleArgument[1]{}
\newcommand\FirstOfOne[1]{#1}
\newcommand\increment[2]{%
  % #1 = number
  % #2 = amount of increments to perform
  \incrementloop{#1}{#2}{#1}{0}%
}%
\newcommand\incrementloop[4]{%
  % #1 = number when the increment-loop was started
  % #2 = amount of increments to perform
  % #3 = number to increment
  % #4 = amount of increments already performed
  \ifnum#4<#2 \expandafter\FirstOfOne\else\expandafter\GobbleArgument\fi
  {%
    \expandafter\PassFirstToSecond\expandafter{\number\numexpr#4+1\relax}{%
      \expandafter\PassFirstToSecond\expandafter{\number\numexpr#3+1\relax}{%
        \PrintAndCallIncrementloop{#1}{#2}%
      }%
    }%
  }%
}%
\newcommand\PrintAndCallIncrementloop[4]{%
  % #1 = number when the increment-loop was started
  % #2 = amount of increments to perform
  % #3 = result of last increment
  % #4 = amount of increments already performed
  Step #4: $#1 + #4 = #3$\par
  \incrementloop{#1}{#2}{#3}{#4}%
}%

\begin{document}
\increment{17}{3}%
\end{document}

在此处输入图片描述


使用尾递归,您不必处理未限定的参数。

您还可以以尾部递归方式处理以逗号分隔的参数列表,并将每个以逗号分隔的参数传递给某些宏以进行进一步处理/检查。

但是,对于带分隔符的参数以及将内容传递给其他宏时,您需要小心剥离/删除可能围绕参数的花括号。

您还可以实现对参数元组列表的处理,但是如果您执行这样的操作,您还需要复杂的例程

  • 检查用户提供的参数的组成部分是否真的与你心中的参数元组模式相匹配,以及
  • 来处理并非如此的情况。

这样的事情在 (La)TeX 中是可行的,但是您需要一定的 (La)TeX 编程经验,并且您需要熟悉 TeX 的宏扩展概念,并且您需要知道 TeX 在收集宏参数时遵循的规则。

您可能对 LaTeX 3 编程接口提供的 (comma-)list-parsing-routines、keyval-parsing-routines 和处理正则表达式的例程感兴趣,而这些例程又可通过 LaTeX 2ε 软件包获得。解释3

您可能对这篇文章感兴趣TeX 口中的列表作者:Alan Jeffrey,TUGboat,第 11 卷(1990 年),第 2 期。(“TeX 食道中的列表”可能是更合适的标题,因为本文重点介绍如何通过扩展可扩展标记来完成任务,同时引用 Knuth 的类比,TeX 是一只长着眼睛和消化道的野兽,可扩展标记的扩展不是在嘴里进行,而是在食道中进行。标记是在嘴里通过“咀嚼”(预处理的).tex 输入文件行创建的,然后被发送到食道。)

应对挑战的答案19 作者名单“ 和 ”21 可变数量的参数Michael Downes 的“围绕 TeXBook 的危险弯道”系列中的“挑战”也许会让您感兴趣。


在上面的两个例子中,一个未定界参数{\relax}被附加到未定界参数序列中,作为序列结束的标记。在 TeX 术语中,这种标记有时被称为“标记参数”/“标记标记”。

宏参数被识别为哨兵参数的属性将尾部递归循环可以处理的宏参数集限制为不具有这些属性的宏参数。

因此,当涉及到列表处理时,我有时会实现没有“标记参数”/“标记标记”的东西,并且作为终止循环的标准,让 TeX 检查包含整个剩余列表的参数是否为空/空白。(“空白”意味着所讨论的参数要么根本没有标记,要么仅由明确的空间标记组成。)


对于诸如此类的事情\if..\else..\fi\csname..\endcsname您需要确保处理事物的顺序不会被那些爱好拆毁纸牌屋的邪恶用户的恶意输入所扰乱。;-)

例如,如果一个心怀恶意的用户在参数的某个地方插入不匹配\fi或不匹配\else或不匹配或类似的内容,这不应该导致宏本身的定义文本中存在的表达式或表达式由于错误匹配或错误匹配而“爆炸”。\endcsname\if..\csname\if..-\else-\fi\csname-\endcsname

还要注意的是,\if..-\else-\fi-nesting 独立于 group-nesting,是通过 进行 group-nesting \begingroup..\endgroup,是通过 进行 group-nesting {...}

如果事情也要在对齐/表格中进行,则需要确保在&整个尾部递归过程终止之前,诸如此类的内容永远不会插入到标记流中而不会嵌套在花括号中。

例如,这很好,因为&不会太早发现:

\documentclass{article}
\begin{document}
\newcommand\exchange[2]{#2#1}
\begin{tabular}{|l|l|}
\hline
lowercase roman: \romannumeral\exchange{&}{20 }arabic: 20\\ %<- & being in braces is not bad here.
\hline
\end{tabular}
\end{document}

结果:

在此处输入图片描述

例如,这很糟糕,因为&发现得太早:

\documentclass{article}
\begin{document}
\newcommand\exchange[2]{#2#1}
\begin{tabular}{|l|l|}
\hline
lowercase roman: \romannumeral\exchange&{20 }arabic: 20\\ %<- & not being in braces is bad here.
\hline
\end{tabular}
\end{document}

结果:

! Missing number, treated as zero.
<to be read again> 
                   \hfil 
l.6 lowercase roman: \romannumeral\exchange&
                                            {20 }arabic:20\\ %<- & not being...


目的是将{a}{b}{c}{d}...任意长度的序列(但为 2 的倍数)传递给命令,\autocite形式如下[a]{b}[c]{d}....

作为一个更复杂的尾递归例程的示例,其中迭代根据参数的空白而终止,下面是一个例程

\transformsequence{⟨tokens to prepend⟩}{⟨tokens in case of error⟩}{⟨sequence⟩}

其工作原理如下:

  • 如果⟨sequence⟩为空/空白:
    ⟨tokens in case of error⟩
  • 如果⟨sequence⟩不包含偶数个无分隔括号嵌套参数:
    ⟨tokens in case of error⟩
  • 如果⟨sequence⟩确实包含偶数个无分隔括号嵌套参数:
    ⟨tokens to prepend⟩[{⟨argument 1⟩}]{⟨argument 2⟩}...[{⟨argument 2k-1⟩}]{⟨argument 2k⟩}

由于\romannumeral-扩展,结果是通过两个扩展步骤/两次命中来得出\transformsequence\expandafter

⟨tokens to prepend⟩例如可以是\autocites

⟨tokens in case of error⟩例如,可以是一些用于引发错误消息的代码。

请注意,在结果序列中,-arguments 的内部[..]嵌套在一对额外的花括号中:[{..}]

]通常,将可选参数/ -分隔的参数嵌套在一对额外的花括号中是一种很好的做法,以防可选参数本身包含某些内容],例如由于嵌套可选参数。
在下面的代码中,我插入了注释,指示将哪两行转换为注释,以防您不想要这些额外的花括号围绕[..]-参数的内部。

\errorcontextlines=10000
\documentclass[a4paper, landscape]{article}

%===================[adjust margins/layout for the example]====================
\advance\paperheight by 5cm\relax
\csname @ifundefined\endcsname{pagewidth}{}{\pagewidth=\paperwidth}%
\csname @ifundefined\endcsname{pdfpagewidth}{}{\pdfpagewidth=\paperwidth}%
\csname @ifundefined\endcsname{pageheight}{}{\pageheight=\paperheight}%
\csname @ifundefined\endcsname{pdfpageheight}{}{\pdfpageheight=\paperheight}%
\textwidth=\paperwidth
\oddsidemargin=1.5cm
\marginparsep=.2\oddsidemargin
\marginparwidth=\oddsidemargin
\advance\marginparwidth-2\marginparsep
\advance\textwidth-2\oddsidemargin
\advance\oddsidemargin-1in
\evensidemargin=\oddsidemargin
\textheight=\paperheight
\topmargin=1.5cm
\footskip=.5\topmargin
{\normalfont\global\advance\footskip.5\ht\strutbox}%
\advance\textheight-2\topmargin
\advance\topmargin-1in
\headheight=0ex
\headsep=0ex
\pagestyle{empty}
\parindent=0ex
\parskip=\medskipamount
\topsep=0ex
\partopsep=0ex
%==================[eof margin-adjustments]====================================

\makeatletter    
%%=============================================================================
%% Paraphernalia:
%%    \UD@firstoftwo, \UD@secondoftwo, \UD@Exchange, \UD@PassFirstToSecond, 
%%    \UD@removespace, \UD@stopromannumeral,
%%    \UD@CheckWhetherNull, \UD@CheckWhetherBlank, \UD@CheckWhetherBrace, 
%%    \UD@CheckWhetherLeadingSpace, \UD@ExtractFirstArg
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@Exchange[2]{#2#1}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\@ifdefinable\UD@removespace{\UD@Exchange{ }{\def\UD@removespace}{}}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
  \expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument is blank (empty or only spaces):
%%-----------------------------------------------------------------------------
%% -- Take advantage of the fact that TeX discards space tokens when
%%    "fetching" _un_delimited arguments: --
%% \UD@CheckWhetherBlank{<Argument which is to be checked>}%
%%                      {<Tokens to be delivered in case that
%%                        argument which is to be checked is blank>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked is not blank>}%
\newcommand\UD@CheckWhetherBlank[1]{%
  \romannumeral\expandafter\expandafter\expandafter\UD@secondoftwo
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo#1{}{}}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument's first token is a catcode-1-character
%%.............................................................................
%% \UD@CheckWhetherBrace{<Argument which is to be checked>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has a leading
%%                        explicit catcode-1-character-token>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked does not have a
%%                        leading explicit catcode-1-character-token>}%
\newcommand\UD@CheckWhetherBrace[1]{%
  \romannumeral\expandafter\UD@secondoftwo\expandafter{\expandafter{%
  \string#1.}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@firstoftwo}{%
  \expandafter\UD@stopromannumeral\UD@secondoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument starts with a space-token
%%.............................................................................
%% \UD@CheckWhetherLeadingExplicitSpace{<Argument which is to be checked>}%
%%                                     {<Tokens to be delivered in case <argument
%%                                       which is to be checked> does have a
%%                                       leading explicit space-token>}%
%%                                     {<Tokens to be delivered in case <argument
%%                                       which is to be checked> does not have a
%%                                       a leading explicit space-token>}%
\newcommand\UD@CheckWhetherLeadingExplicitSpace[1]{%
  \romannumeral\UD@CheckWhetherNull{#1}%
  {\expandafter\UD@stopromannumeral\UD@secondoftwo}%
  {%
    % Let's nest things into \UD@firstoftwo{...}{} to make sure they are nested in braces
    % and thus do not disturb when the test is carried out within \halign/\valign:
    \expandafter\UD@firstoftwo\expandafter{%
      \expandafter\expandafter\expandafter\UD@stopromannumeral
      \romannumeral\expandafter\UD@secondoftwo
      \string{\UD@CheckWhetherLeadingExplicitSpaceB.#1 }{}%
    }{}%
  }%
}%
\@ifdefinable\UD@CheckWhetherLeadingExplicitSpaceB{%
  \long\def\UD@CheckWhetherLeadingExplicitSpaceB#1 {%
    \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
    {\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
    {\expandafter\expandafter\expandafter\UD@stopromannumeral
     \expandafter\expandafter\expandafter}%
     \expandafter\UD@secondoftwo\expandafter{\string}%
  }%
}%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%
%%   \UD@ExtractFirstArg{ABCDE} yields  {A}
%%
%%   \UD@ExtractFirstArg{{AB}CDE} yields  {{AB}}
%%
%% Due to \romannumeral-expansion the result is delivered after two 
%% expansion-steps/after "hitting" \UD@ExtractFirstArg with \expandafter
%% twice.
%%
%% \UD@ExtractFirstArg's argument must not be blank.
%% This case can be cranked out via \UD@CheckWhetherBlank before calling
%% \UD@ExtractFirstArg.
%%
%% Uses frozen-\relax as delimiter for speeding things up.
%% I chose frozen-\relax because David Carlisle pointed out in
%% <https://tex.stackexchange.com/a/578877>
%% that frozen-\relax cannot be (re)defined in terms of \outer and cannot be
%% affected by \uppercase/\lowercase.
%%
%% \UD@ExtractFirstArg's argument may contain frozen-\relax:
%% The only effect is that internally more iterations are needed for
%% obtaining the result.
%%
%%.............................................................................
\@ifdefinable\UD@RemoveTillFrozenrelax{%
  \expandafter\expandafter\expandafter\UD@Exchange
  \expandafter\expandafter\expandafter{%
  \expandafter\expandafter\ifnum0=0\fi}%
  {\long\def\UD@RemoveTillFrozenrelax#1#2}{{#1}}%
}%
\expandafter\UD@PassFirstToSecond\expandafter{%
  \romannumeral\expandafter
  \UD@PassFirstToSecond\expandafter{\romannumeral
    \expandafter\expandafter\expandafter\UD@Exchange
    \expandafter\expandafter\expandafter{%
    \expandafter\expandafter\ifnum0=0\fi}{\UD@stopromannumeral#1}%
  }{%
    \UD@stopromannumeral\romannumeral\UD@ExtractFirstArgLoop
  }%
}{%
  \newcommand\UD@ExtractFirstArg[1]%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  {\UD@stopromannumeral#1}%
  {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillFrozenrelax#1}}%
}%
%%=============================================================================
%% \transformsequence{<tokens to prepend>}{<tokens in case of error>}{<sequence>}
%%
%% If <sequence> does not hold an even amount of undelimited brace-nested
%% arguments:
%%    <tokens in case of error>
%%
%% If <sequence> hold an even amount of undelimited brace-nested arguments:
%%    <tokens to prepend>[{element 1}]{elelent 2}...[{element 2k-1}]{elelent2k}
%%
%% If <sequence> is empty/blank:
%%    <tokens in case of error>
%%
%% Due to \romannumeral-expansion the result is delivered by two expansion-steps/
%% by having \transformsequence hit by \expandafter twice.
%%
\newcommand\transformsequence[3]{%
  \romannumeral\UD@CheckWhetherBlank{#3}{\UD@stopromannumeral#2}{%
    \transformsequenceloopOpt{}{#1}{#2}{#3}%
  }%
}%
\newcommand\transformsequenceloopOpt[4]{%
  % #1 = <transformed sequence gathered so far>
  % #2 = <tokens to prepend>
  % #3 = <tokens in case of error>
  % #4 = <remaining sequence to transform>
  \UD@CheckWhetherBlank{#4}{\UD@stopromannumeral#2#1}{%
     \UD@CheckWhetherLeadingExplicitSpace{#4}{%
        \expandafter\UD@PassFirstToSecond\expandafter{\UD@removespace#4}{\transformsequenceloopOpt{#1}{#2}{#3}}%
     }{%
       \UD@CheckWhetherBrace{#4}{%
         \expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#4}{%
           \expandafter\UD@PassFirstToSecond\expandafter{%
             \romannumeral\expandafter\expandafter\expandafter\UD@Exchange
             \expandafter\expandafter\expandafter{% <-If you don't want the square bracket-args in extra curly braces comment out this line.
               \UD@ExtractFirstArg{#4}%
             }% <-If you don't want the square bracket-args in extra curly braces also comment out this line.
             {\UD@stopromannumeral#1[}]%
           }{\transformsequenceloopNoOpt}{#2}{#3}%
         }%
       }{%
         \UD@stopromannumeral#3%
       }%
     }%
  }%
}%
\newcommand\transformsequenceloopNoOpt[4]{%
  % #1 = <transformed sequence gathered so far>
  % #2 = <tokens to prepend>
  % #3 = <tokens in case of error>
  % #4 = <remaining sequence to transform>
  \UD@CheckWhetherBlank{#4}{\UD@stopromannumeral#3}{%
     \UD@CheckWhetherLeadingExplicitSpace{#4}{%
        \expandafter\UD@PassFirstToSecond\expandafter{\UD@removespace#4}{\transformsequenceloopNoOpt{#1}{#2}{#3}}%
     }{%
       \UD@CheckWhetherBrace{#4}{%
         \expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#4}{%
           \expandafter\UD@PassFirstToSecond\expandafter{%
             \romannumeral\expandafter\expandafter\expandafter\UD@Exchange
             \expandafter\expandafter\expandafter{%
               \UD@ExtractFirstArg{#4}%
             }{\UD@stopromannumeral#1}%
           }{\transformsequenceloopOpt}{#2}{#3}%
         }%
       }{%
         \UD@stopromannumeral#3%
       }%
     }%
  }%
}%
\makeatother

\begin{document}

\fbox{Everything okay:}

\begin{verbatim}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \transformsequence{\autocites}%
                    {tokens in case of error}%
                    { {a}{b}{c}{d}{e}{f}{g} {h} {i}{j}{k}{l} {m} {n}{o}{p}{q}{r}{s}{t}{u}{v}{w}{x}{y}{z} }%
}%
\end{verbatim}

yields:

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \transformsequence{\autocites}%
                    {tokens in case of error}%
                    { {a}{b}{c}{d}{e}{f}{g} {h} {i}{j}{k}{l} {m} {n}{o}{p}{q}{r}{s}{t}{u}{v}{w}{x}{y}{z} }%
}%
\texttt{\string\test: \meaning\test}%

\hrulefill

\fbox{An odd number of elements, thus: tokens in case of error:}

\begin{verbatim}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \transformsequence{\autocites}%
                    {tokens in case of error}%
                    {{a}{b}{c}{d}{e}{f}{g}{h}{i}{j}{k}{l}{m}{n}{o}{p}{q}{r}{s}{t}{u}{v}{w}{x}{y}}%
}%
\end{verbatim}

yields:

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \transformsequence{\autocites}%
                    {tokens in case of error}%
                    {{a}{b}{c}{d}{e}{f}{g}{h}{i}{j}{k}{l}{m}{n}{o}{p}{q}{r}{s}{t}{u}{v}{w}{x}{y}}%
}%
\texttt{\string\test: \meaning\test}%

\hrulefill

\fbox{Element k is not in braces, thus: tokens in case of error:}

\begin{verbatim}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \transformsequence{\autocites}%
                    {tokens in case of error}%
                    {{a}{b}{c}{d}{e}{f}{g}{h}{i}{j}k{l}{m}{n}{o}{p}{q}{r}{s}{t}{u}{v}{w}{x}{y}}%
}%
\end{verbatim}

yields:

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \transformsequence{\autocites}%
                    {tokens in case of error}%
                    {{a}{b}{c}{d}{e}{f}{g}{h}{i}{j}k{l}{m}{n}{o}{p}{q}{r}{s}{t}{u}{v}{w}{x}{y}}%
}%
\texttt{\string\test: \meaning\test}%

\hrulefill

\fbox{No elements at all, thus: tokens in case of error:}

\begin{verbatim}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \transformsequence{\autocites}%
                    {tokens in case of error}%
                    { }%
}%
\texttt{\string\test: \meaning\test}%
\end{verbatim}

yields:

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \transformsequence{\autocites}%
                    {tokens in case of error}%
                    { }%
}%
\texttt{\string\test: \meaning\test}%

\end{document}

在此处输入图片描述

答案2

最简单的方法是将您想要传递的内容括\autocites在另一个命令中的括号中:\NewDocumentCommand{\myCitation}{m}{\autocites#1}。因此您可以使用\myCitation{{test1}[35]{test2}{test3}}

另一个解决方案仅返回\autocite

\NewDocumentCommand{\MyCitation}{>{\SplitList{;}}m}{%
    \def\processCite##1{\foreach \k/\p in {##1}{\ifthenelse{\equal{\k}{\p}}{\autocite{\k}}{\autocite[\p]{\k}}}}
    \ProcessList{#1}{\processCite}
}

但在提出论点时必须小心:

\MyCitation{test1/35;test2;test3/32}

您需要xparse同时适用于这两种解决方案的软件包以及ifthen适用tikz于第二种解决方案的软件包。

答案3

我认为biblatex具有\autocites最直观的界面,可以完成您希望新宏执行的工作。因此,我不会考虑创建包装器\autocites,我会尝试模拟\autocites不支持它的引用包。

此时,我可能应该警告你,我不希望出版商对你序言中的复杂自定义宏有好感。根据他们的确切工作流程,像这样的宏可能会有问题,这就是为什么通常建议在期刊投稿中尽可能少地使用额外的软件包和复杂的自定义宏。

下面的代码取自于biblatex.sty多引用命令的代码。biblatex的实现相当通用,我们可以通过少许修改来重复使用它。

\autocites如果引用不支持,您还需要弄清楚如何模仿。在下面的 MWE 中,我natbib使用了 ,\citealp因为它看起来不错,但是如果您没有natbib可用的,则可能需要进行额外的工作。

\documentclass[british]{article}
\usepackage[T1]{fontenc}
\usepackage{babel}

\usepackage{natbib}

% need etoolbox's helper macros
\usepackage{etoolbox}

\makeatletter
% code taken and adapted from biblatex.sty

% helper macros
% {<macro>}[<arg1>][<arg2>]{<arg3>}
% => <macro>{<arg1>}{<arg2>}{<arg3>}
\protected\def\emblx@citeargs#1{%
  \@ifnextchar[%]
    {\emblx@citeargs@i{#1}}
    {\emblx@citeargs@iii{#1{}{}}}}
\long\def\emblx@citeargs@i#1[#2]{%
  \@ifnextchar[%]
    {\emblx@citeargs@ii{#1{#2}}}
    {\emblx@citeargs@iii{#1{}{#2}}}}
\long\def\emblx@citeargs@ii#1[#2]{%
  \emblx@citeargs@iii{#1{#2}}}
\long\def\emblx@citeargs@iii#1#2{%
  #1{#2}}

% {<macro>}(<arg1>)(<arg2>)
% => <macro>{<arg1>}{<arg2>}
\protected\def\emblx@multiargs#1{%
  \@ifnextchar(%)
    {\emblx@multiargs@i{#1}}
    {#1{}{}}}
\long\def\emblx@multiargs@i#1(#2){%
  \@ifnextchar(%)
    {\emblx@multiargs@ii{#1{#2}}}
    {#1{}{#2}}}
\long\def\emblx@multiargs@ii#1(#2){#1{#2}}

\newtoggle{emblx@spacegobbled}

\newcommand*{\autocites}{\autocites@bb{}{}}

\newcommand*{\autocites@bb}[2]{%
  \let\emblx@autocites@first\@empty
  \togglefalse{emblx@spacegobbled}%
  \ifblank{#1}
    {\undef\emblx@prenote}
    {\def\emblx@prenote{#1}}%
  \ifblank{#2}
    {\undef\emblx@postnote}
    {\def\emblx@postnote{#2}}%
  \autocitesopen
  \ifundef\emblx@prenote
    {}
    {\emblx@prenote\autocitesprenotedelim}%
  \autocites@go}

\newcommand*{\autocites@go}{%
  \emblx@multiparse}

\newcommand*{\autocites@end}{%
  \ifundef\emblx@postnote
    {}
    {\autocitespostnotedelim\emblx@postnote}%
  \autocitesclose
  \iftoggle{emblx@spacegobbled}
    {\space}
    {}}

\newcommand*{\emblx@multicite@add}[3]{%
  \ifundef\emblx@autocites@first
    {\autocitesmulticitedelim}
    {\undef\emblx@autocites@first}%
  \togglefalse{emblx@spacegobbled}%
  \autocitescite[#1][#2]{#3}%
  \emblx@multiparse}

\def\emblx@multiparse{%
  \futurelet\@let@token\emblx@multiparse@i}

\def\emblx@multiparse@i{%
  \ifx\@let@token\relax
    \emblx@multiparse@ii{\autocites@end{}}%
  \fi
  \ifx\@let@token[%]
    \emblx@multiparse@ii{\emblx@citeargs\emblx@multicite@add}%
  \fi
  \ifx\@let@token\bgroup
    \emblx@multiparse@ii{\emblx@citeargs\emblx@multicite@add}%
  \fi
  \ifx\@let@token\@sptoken
    \emblx@multiparse@ii\emblx@multiparse@iii
  \fi
  \iftrue
    \emblx@multiparse@ii{\autocites@end}%
  \fi
  \emblx@multiparse@qrk}

\def\emblx@multiparse@ii#1#2\emblx@multiparse@qrk{\fi#1}
\csdef{emblx@multiparse@iii} {\toggletrue{emblx@spacegobbled}\emblx@multiparse}
\makeatother

\newcommand*{\autocitescite}{\citealp}
\newcommand*{\autocitesopen}{[}
\newcommand*{\autocitesclose}{]}
\newcommand*{\autocitesprenotedelim}{ }
\newcommand*{\autocitespostnotedelim}{, }
\newcommand*{\autocitesmulticitedelim}{, }


\begin{filecontents}{\jobname.bib}
@book{elk,
  author    = {Anne Elk},
  title     = {A Theory on Brontosauruses},
  year      = {1972},
  publisher = {Monthy \& Co.},
  location  = {London},
}
@book{belk,
  author    = {Anne Belk},
  title     = {A Theory on Brontosauruses},
  year      = {1973},
  publisher = {Monthy \& Co.},
  location  = {London},
}
\end{filecontents}

\begin{document}
Lorem \autocites[12]{elk}[cf.][13]{belk}{article-full}
ipsum \autocites[14]{elk}
dolor \autocites{elk}
sit \autocites[cf.][]{elk}
amet X\autocites[cf.][15]{elk}Y

\bibliographystyle{plainnat}
\bibliography{\jobname,xampl}
\end{document}

Lorem [Elk, 1972, 12,参见Belk, 1973, 13, Aamport, 1986] ipsum [Elk, 1972, 14] dolor [Elk, 1972] sit [cf. Elk, 1972] amet X[参见Elk,1972,15]Y

相关内容