5

We'll be using this neat code from User Werner's answer from the OP "Macro - Repeat the pattern for any (even) number of arguments":

\documentclass{article}

\makeatletter
\newcommand{\ByTwo}{\@ifnextchar\StopByTwo\relax\@ByTwo}
\newcommand{\@ByTwo}[2]{ {\bfseries #1} {`#2'} \ByTwo}
\makeatother
\let\StopByTwo\relax

\begin{document}

\ByTwo{A}{B}\StopByTwo

\ByTwo{A}{B}{C}{D}{E}{F}\StopByTwo

\ByTwo ABCDEF\StopByTwo

\end{document}

It produces the following:

From Werner's answer

Now, let's say that we want to introduce an extra command (e.g. \endgraf) at the end of every pair, but not for the last pair. In other words: between every pair.

How could this be achieved please?

O0123
  • 1,773

6 Answers6

6

Building on the same theme of the ConTeXt solution that I posted for your previous question, you can use \doquadruplegroupempty from ConTeXt:

\unexpanded\def\ByTwo
    {\doquadruplegroupempty\doByTwo}

\def\doByTwo#1#2%
    {{\bf #1} ‘#2’
     \ifthirdargument
       --% Replace by whatever you want
       \expandafter\ByTwo
     \fi}

\starttext
\startlines
\ByTwo{A}{B}
\ByTwo{A}{B}{C}{D}
\ByTwo{A}{B}{C}{D}{E}{F}
\stoplines
\stoptext

which gives

enter image description here

The same code works in LaTeX, if you copy the definition of \doquadrupleargument from ConTeXt. Below I have included a slightly simplified code from syst-gen.mkii:

\documentclass{article}
\newif\iffirstargument
\newif\ifsecondargument
\newif\ifthirdargument
\newif\iffourthargument


\def\dodogetgroupargument
  {\ifx\nextargument\bgroup
     \def\nextargument{\dogroupargumentyes\dodogetargument}%
   \else
       \def\nextargument{\dogroupargumentnop\dodogetargument{}}%
   \fi
   \nextargument}%

\def\dogetgroupargument#1#2%
  {\let\dogroupargumentyes#1%
   \let\dogroupargumentnop#2%
   \futurelet\nextargument\dodogetgroupargument}


\def\doquadruplegroupempty#1%
  {\def\dodogetargument##1%
     {\def\dodogetargument####1%
        {\def\dodogetargument########1%
           {\def\dodogetargument%
              {#1{##1}{####1}{########1}}%
            \dogetgroupargument\fourthargumenttrue\fourthargumentfalse}%
         \dogetgroupargument\thirdargumenttrue\thirdargumentfalse}%
      \dogetgroupargument\secondargumenttrue\secondargumentfalse}%
   \dogetgroupargument\firstargumenttrue\firstargumentfalse}

\protect\def\ByTwo
    {\doquadruplegroupempty\doByTwo}

\def\doByTwo#1#2%
    {{\bfseries #1} `#2'
     \ifthirdargument
       -- % Replace by whatever you want
       \expandafter\ByTwo
     \fi}


\begin{document}

\ByTwo{A}{B} 

\ByTwo{A}{B}{C}{D} 

\ByTwo{A}{B}{C}{D}{E}{F}
\end{document}
Aditya
  • 62,301
  • @VincentMiaEdieVerheyen: Note that Werner's solution (which relies on a \Stop... macrois different from mine). My solutions doe not use any feature ofluatex`. You just have to copy the definition of appropriate macros from syst-aux.mkiv to LaTeX – Aditya Oct 07 '17 at 12:06
  • Note that @wipet's solution is much easier to port to LaTeX (should work as is) and achieves the same behavior as my solution. – Aditya Oct 07 '17 at 12:09
  • @VincentMiaEdieVerheyen notice that both Aditya's and wipet's solutions require no space between the braced letters, which is a bit unexpected behaviour in LaTeX. Try \ByTwo{A}{B}{C}<space here>{D}{E}{F}. In LaTeX expected behaviour from user is that the space should not influence output. For wipet's solution try \byTwo{A}{B}{C}{D}<space here>{E}{F}. –  Oct 07 '17 at 13:17
  • Also neither solution works with \ByTwo ABCDEF\relax example of OP. Only saying that so that passers-bye get full information ;-) –  Oct 07 '17 at 13:28
  • yes, this is explicitly checking for opening brace, so not giving a brace will not work. – Aditya Oct 07 '17 at 16:22
4

You can insert content between every pair by using a cunning delay tactic:

enter image description here

\documentclass{article}

\newcommand{\pairdiff}{--}
\makeatletter
% Delay the use of \pairdiff by one \ByTwo (https://tex.stackexchange.com/a/89187/5764)
\def\@newpair{\def\@newpair{\pairdiff}}
\newcommand{\ByTwo}{\@ifnextchar\StopByTwo{\def\@newpair{\def\@newpair{\pairdiff}}}\@ByTwo}
\newcommand{\@ByTwo}[2]{\@newpair{\bfseries #1} {`#2'} \ByTwo}
\makeatother
\let\StopByTwo\relax

\begin{document}

\ByTwo{A}{B}\StopByTwo

\ByTwo{A}{B}{C}{D}{E}{F}\StopByTwo

\ByTwo ABCDEF\StopByTwo

\end{document}

\pairdiff contains whatever you want to insert between the pairs to differentiate them.

Werner
  • 603,163
3

First I define an infrastructure: a command \recurseover that takes as arguments a two-argument macro and the text to be later inserted between any two items; the command will then absorb everything between | and | (change the delimiter if it doesn't suit you) and scans pairs of items until finishing.

\documentclass{article}
\usepackage{xparse}

% The infrastructure
\ExplSyntaxOn
\NewDocumentCommand{\recurseover}{mm}
 { % #1 = command to repeat, #2 = what to insert
  \vincent_recurseover:nnw { #1 } { #2 }
 }

\cs_new:Npn \vincent_recurseover:nnw #1 #2 | #3 |
 {
  \int_compare:nNnTF { \int_mod:nn { \tl_count:n { #3 } } { 2 } } = { 0 }
   {
    \__vincent_recurseover:nnnn { #1 } { #2 } #3 \q_mark\q_stop
   }
   {
    \__vincent_recurseover:nnnn { #1 } { #2 } #3 {} \q_mark\q_stop
   }
 }

\cs_new:Nn \__vincent_recurseover:nnnn
 {
  \tl_if_eq:nnF { #3 } { \q_mark }
   { % we do have args
     #1 { #3 } { #4 } \__vincent_recurseover_next:nnnn { #1 } { #2 }
   }
 }
\cs_new:Nn \__vincent_recurseover_next:nnnn
 {
  \tl_if_eq:nnF { #3 } { \q_mark }
   {
    #2 #1 { #3 } { #4 } \__vincent_recurseover_next:nnnn { #1 } { #2 }
   }
 }
\ExplSyntaxOff

\newcommand{\processtwo}[2]{\textbf{#1} `#2'}% or whatever
\newcommand{\multiprocesstwo}{\recurseover{\processtwo}{-X-}}

\begin{document}

\processtwo{X}{AAA}

Empty:
\recurseover{\processtwo}{-X-}||
or
\multiprocesstwo||

One:
\recurseover{\processtwo}{-X-}|{X}{AAA}|
or
\multiprocesstwo|{X}{AAA}|

Two:
\recurseover{\processtwo}{-X-}|{X}{AAA}{Y}{BBB}|
or
\multiprocesstwo|{X}{AAA}{Y}{BBB}|

Three:
\recurseover{\processtwo}{-X-}|{X}{AAA}{Y}{BBB}{Z}{CCC}|
or
\multiprocesstwo|{X}{AAA}{Y}{BBB}{Z}{CCC}|

Odd number:
\recurseover{\processtwo}{-X-}|{X}{AAA}{Y}{BBB}{Z}|
or
\multiprocesstwo|{X}{AAA}{Y}{BBB}{Z}|

\end{document}

The command can be called directly or by defining a wrapper macro, as you see.

enter image description here

egreg
  • 1,121,712
3

Using TeX primitives, your problem can be solved as follows:

\def\byTwo{\def\byTwoS{\def\byTwoS{ -- }}\futurelet\next\byTwoA}
\def\byTwoA{\ifx\bgroup\next\expandafter\byTwoB\fi}
\def\byTwoB#1#2{\byTwoS{\bf#1} `#2'\futurelet\next\byTwoA}

\byTwo{A}{B}{C}{D}{E}{F}

\byTwo{A}{B}

\end

Note, that native TeX language is very compact. We need only three lines of macros to solve your problem.

wipet
  • 74,238
2

A slight reorganization of your code allows an extension with minimal effort.

\documentclass{article}

\makeatletter
\newcommand{\ByTwo}[2]{{\bfseries #1} {`#2'}\@ByTwo}
\newcommand{\@ByTwo}{\@ifnextchar\relax{}{\PairSep\ByTwo}}
\makeatother

\newcommand{\PairSep}{ -- }% customize as desired

\usepackage{color}

\begin{document}

\ByTwo{A}{B}\relax

\ByTwo{A}{B}{C}{D}{E}{F}\relax

\ByTwo ABCDEF\relax

\renewcommand{\PairSep}{\textcolor{blue}{::::::}}

\ByTwo ABCDEF\relax

\end{document}

enter image description here

  • I am using LaTeX's \@ifnextchar as it is in original and at least one other answer, but this makes the looping over non expandable. If you need the loop to "prepare the result" rather than typeset it immediately, some expandable looping could come handy; the non-expandable code can of course be recycled to fill-up some macro with final result. –  Oct 06 '17 at 08:11
  • It is here author-assumed \@ifnextchar behaviour that it will gobble spaces, so \ByTwo ABCDEF\relax, \ByTwo A BCDEF\relax, \ByTwo AB CDEF\relax, \ByTwo A B C D E F \relax, etc... all behave the same. And of course the arguments may be braced, as is more usual LaTeX syntax. –  Oct 07 '17 at 13:31
2
\documentclass{article}
\usepackage{listofitems}
\def\ByTwo#1#2#3\StopByTwo{%
  \textbf{#1} `#2'\ifx\relax#3\else\ $\bullet$ \ByTwo#3\StopByTwo\fi}
\begin{document}
\ByTwo{A}{B}\StopByTwo

\ByTwo{A}{B}{C}{D}{E}{F}\StopByTwo

\ByTwo ABCDEF\StopByTwo
\end{document}

enter image description here