2

I am working quite often with LaTeX and reuse text snippets from time to time. However, different publisher use different packages and backends for citing. So I found the idea handy to wrap the specific commands into more generic ones. But I never solved the problem of feeding an arbitrary number of sources to a command. I came across this very nice post on generating sequences automatically but I am stuck because I need to generate the sequence like {,test1,35,test2,,test3}=>{}{test1}{35}{test2}{test3} before issuing the command \autocites... (or \parencites). This post seems to fit the problem even better but again it produces just a sequence of commands rather than a sequence of inputs to a single command.

\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}

compiled LaTeX-PDF

The MVE assumes a bib-file sample.bib with the content:

@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}
}

The objective is to pass a sequence {a}{b}{c}{d}... of arbitrary length (but being a multiple of 2) to the command \autocite in the form [a]{b}[c]{d}....

Perhaps this is the wrong approach building the command before issuing it. Any ideas?

0
3

But I never solved the problem of feeding an arbitrary number of sources to a command.

If with "arbitrary number of sources" you mean "arbitrary amount of undelimited arguments", then one aspect of the answer can be the idea of tail-recursion.

In TeX a so-called tail-recursive macro (or a tail-recursive macro mechanism) terminates by calling itself with modified/different arguments unless the arguments fulfill some condition for terminating the recursion.

E.g., the following macro \GreetingsToPeopleLoop processes a non-delimited argument as follows:

  • If the argument's first token's meaning does not equal the meaning of the \relax-primitive, then deliver the argument, prepended with Hello, and trailed by !\par and call \GreetingsToPeopleLoop again.
  • If the argument's first token's meaning does equal the meaning of the \relax-primitive, then do nothing.

This way recursively a sequence of undelimited arguments can be processed:

\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}

enter image description here

\expandafter is used to have \fi processed before processing \GreetingsToPeopleLoop. \fi is expandable and is removed from the token-stream during being processed without delivering replacement-tokens in return. Thus \fi is not "in the way" any more when the undelimited argument of \GreetingsToPeopleLoop is gathered.
Also important: As \fi is already processed, it is removed from the input-stack.
With tail-recursive macros always ensure that all \else and \fi are processed before calling the tail-recursive macro for the next iteration. Otherwise tokens belonging to unprocessed \else/\fi-branches that are to be discarded might accumulate in the input-stack until you get a ! TeX capacity exceeded, sorry -error.

With this approach, however, an evil user can easily provoke an error-message about ! Extra \else. and other error-messages about ! Extra \fi.:

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

Of course you can do:

\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}

enter image description here


This was a very simple example of tail-recursively processing a list of undelimited arguments.

You can also apply tail-recursion for having carried out algorithms where macro-arguments serve as variables that get modified by the tail-recursion-macro itself.
In such contexts exchanging macro-arguments or having "helper-macros" put macro-arguments (nested in braces) behind other macro-arguments is handy.

With the following example the macro-mechanism \increment{<number>}{<amount of increments>} increments a given integer number several times and each time prints the result:

\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}

enter image description here


With tail-recursion you are not bound to processing undelimited arguments.

You can also process lists of comma-delimited arguments tail-recursively, and pass each comma-delimited argument to some macro for further processing/examination.

But with delimited arguments and when passing things on to other macros, you need to be picky about the stripping/removal of curly braces that might surround arguments.

You can also implement the processing of a list of tuples of arguments but if you do such things you also need sophisticated routines

  • for checking if the components of the user-provided argument really match the pattern(s) you have in mind for your argument-tuples, and
  • for handling the situation of this not being the case.

Such things are feasible in (La)TeX but you need a certain amount of experience in (La)TeX-programming and you need to be familiar to TeX's concepts of macro-expansion and you need to know the rules which TeX obeys/follows when gathering arguments for macros.

You might be interested in the (comma-)list-parsing-routines and the keyval-parsing-routines and the routines for handling regular expressions provided by the programming-interface of LaTeX 3 which in turn is made available within LaTeX 2ε via the package expl3.

You might be interested in the article Lists in TeX's mouth by Alan Jeffrey, TUGboat, Volume 11 (1990), No. 2. ("Lists in TeX's gullet" might have been a more appropriate title because this article focuses on getting things done via expansion of expandable tokens while, referencing Knuth's analogy of TeX being a beast with eyes and a digestive tract, expansion of expandable tokens does not take place in the mouth but does take place in the gullet. Tokens are created in the mouth by "chewing" (pre-processed) lines of files of .tex-input and are sent to the gullet.)

The answers to Challenges "19 Author lists" and "21 Variable number of arguments" of Michael Downes' collection of "Around The Dangerous Bend of the TeXBook"-challenges might be of interest to you as well.


In the first two examples above an undelimited argument {\relax} was attached to the sequence of undelimited arguments as a marker for the end of the sequence. Such markers sometimes are called "sentinel arguments"/"sentinel tokens" in TeX-jargon.

The properties by which a macro-argument is recognized as a sentinel-argument restrict the set of macro-arguments that can be processed by the tail-recursive loop to such macro-arguments that do not have these properties.

Therefore when it comes to list-processing I sometimes implement things without "sentinel arguments"/"sentinel tokens" and, as a criterion for terminating the loop, have TeX check the emptiness/blankness of an argument holding the entire remaining list. ("blankness" means that the argument in question either has no tokens at all or consists of explicit space tokens only.)


With things like \if..\else..\fi and \csname..\endcsname you need to ensure that the order of processing things cannot be disturbed by malicious input of an evil user whose hobby is bringing down houses of cards. ;-)

E.g., if an evil-minded user inserts an unmatched \fi or an unmatched \else or an unmatched \endcsname or the like somewhere in an argument, this should not cause \if.. expressions or \csname expressions present in the definition text of the macro itself to "blow up" due to erroneous \if..-\else-\fi-matching or erroneous \csname-\endcsname-matching.

Also be aware that \if..-\else-\fi-nesting is independent from group-nesting, be the group-nesting via \begingroup..\endgroup, be the group-nesting via {...}.

If things are to work out within alignments/tables as well, you need to ensure that & and the like do never get inserted into the token-stream without being nested into curly braces until the entire tail-recursive-process is terminated.

For example, this is good as the & is not found too early:

\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}

Result:

enter image description here

For example, this is bad as the & is found too early:

\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}

Result:

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


The objective is to pass a sequence {a}{b}{c}{d}... of arbitrary length (but being a multiple of 2) to the command \autocite in the form [a]{b}[c]{d}....

As an example for a more sophisticated tail-recursive routine where iteration terminates depending on the blankness of an argument, here is a routine

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

which works as follows:

  • If ⟨sequence⟩ is empty/blank:
    ⟨tokens in case of error⟩
  • If ⟨sequence⟩ does not hold an even amount of undelimited brace-nested arguments:
    ⟨tokens in case of error⟩
  • If ⟨sequence⟩ does hold an even amount of undelimited brace-nested arguments:
    ⟨tokens to prepend⟩[{⟨argument 1⟩}]{⟨argument 2⟩}...[{⟨argument 2k-1⟩}]{⟨argument 2k⟩}

Due to \romannumeral-expansion the result is delivered by two expansion-steps/by having \transformsequence hit by \expandafter twice.

⟨tokens to prepend⟩ can, e.g., be \autocites.

⟨tokens in case of error⟩ can, e.g., be some code for raising an error-message.

Be aware that in the resulting sequence the inner of [..]-arguments is nested in an extra pair of curly braces: [{..}]

Usually it is good practice to nest optional arguments/]-delimited arguments in an extra pair of curly braces in case the optional argument itself is to contain something with ], e.g., due to nesting optional arguments.
In the code below I inserted comments indicating which two lines to turn into comments in case you don't want these extra curly braces surrounding the inner of [..]-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}

enter image description here

1
  • great! I can't emphasize enough, how much I have learned from this post! Although being an active user of LaTeX, pgf & tikz for eight years now, it humbles me how much I can still learn. I'll try it with this. Thx for all the effort! – max Jan 7 at 7:10
1

The simplest way is to enclose what you want to pass to \autocites within braces in another command: \NewDocumentCommand{\myCitation}{m}{\autocites#1}. Therefore you can use \myCitation{{test1}[35]{test2}{test3}}.

Another solution, which return only \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}
}

but you have to take care when giving the arguments:

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

You need xparse package for both solutions, and ifthen and tikz for the second one.

3
  • thx for the swift reply! The first idea is a great workaround (+1) but may not be as universal as I intended (if one needs to use \citep instead of \parencites, they still need to rewrite every command). So I appreciate the second approach, unfortunately it leads to the same problem as I did before: it produces a sequence of \autocite commands yielding to [1, 35][2][3, 32] rather than [1, 2, p. 35]. (BTW, only the pgffor-package is required from tikz) – max Jan 4 at 12:19
  • I didn't remember who belonged foreach… Thanks! – NBur Jan 4 at 14:51
  • I'm unable to pass the content of \citations as argument(s) of \autocite ``` \gdef\citations{} \makeatletter \NewDocumentCommand{\processCiteII}{>{\SplitList{/}}m}{% \ifthenelse{\equal{\@secondoftwo#1\relax}{\relax}}% {\edef\citations{\citations{\@firstoftwo#1\relax}}} {\edef\citations{\citations[\@secondoftwo#1\relax]{\@firstoftwo#1\relax}}} } \makeatother \NewDocumentCommand{\MyCitationII}{>{\SplitList{;}}m}{% \ProcessList{#1}{\processCiteII} \citations% prints ok \meaning\citations% not correctly expanded… \autocites\citations \def\citations{} } ``` – NBur Jan 4 at 14:53
1

I think that biblatex's \autocites has the most intuitive interface for the job you want your new macro to do. So I wouldn't look into creating a wrapper around \autocites, I would try to emulate \autocites for citation packages that do not support it.

At this point I should probably warn you that I don't expect publishers to look favourably upon complex custom macros in your preamble. Depending on their exact workflow macros like this can be problematic, that's why it is usually advisable to use as little extra packages and complex custom macros as possible for journal submissions.

The code below is taken from biblatex.sty's code for multicite commands. biblatex's implementation is fairly general and we can reuse it with small changes.

You also need to figure out how you'd like to emulate \autocites if the citation does not support it. In the MWE below I used natbib's \citealp because it looked nice, but if you don't have natbib available, additional work may be necessary.

\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, cf. Belk, 1973, 13, Aamport, 1986] ipsum [Elk, 1972, 14] dolor [Elk, 1972] sit [cf. Elk, 1972] amet X[cf. Elk, 1972, 15]Y

1
  • Thx. You are probably right about that publishers will only show limited appreciation of such hacks. Originally, I thought that it is a standard procedure, which I am only incapable of googeling correctly xD Nonetheless, I appreciate all suggestions, of which I can learn=) – max Jan 7 at 7:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.