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?

6 Answers 6

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.

4
  • The OP included \ByTwo ABCDEF\StopByTwo input syntax as example, but \byTwo ABCDEF fails to give corresponding expected output.
    – user4686
    Oct 7, 2017 at 13:26
  • @jfbu I did read this comment from OP: "No, the arguments will not always consist of single letters. They are just used as dummies. Yes, they could always be enclosed in curly braces. Yes, it will be guaranteed that there are an even number of arguments."
    – wipet
    Oct 8, 2017 at 15:08
  • fair enough ;-)
    – user4686
    Oct 9, 2017 at 11:44
  • @wipet Your command works fine on its own, but I am trying to incorporate it within an Environment (thus of a higher level in hierarchy) and then I get an "Undefined control sequence" error. Please have a look at the new OP concerning this problem entitled "A Command works fine when (defined) outside of a LaTeX3 Environment, but yields “Undefined control sequence” when (defined) inside the Environment".
    – O0123
    Oct 9, 2017 at 15:04
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}
5
  • @VincentMiaEdieVerheyen: Note that Werner's solution (which relies on a \Stop... macro` is different from mine). My solutions doe not use any feature of luatex. You just have to copy the definition of appropriate macros from syst-aux.mkiv to LaTeX
    – Aditya
    Oct 7, 2017 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 7, 2017 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}.
    – user4686
    Oct 7, 2017 at 13:17
  • Also neither solution works with \ByTwo ABCDEF\relax example of OP. Only saying that so that passers-bye get full information ;-)
    – user4686
    Oct 7, 2017 at 13:28
  • yes, this is explicitly checking for opening brace, so not giving a brace will not work.
    – Aditya
    Oct 7, 2017 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.

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

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

2
  • 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.
    – user4686
    Oct 6, 2017 at 8: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.
    – user4686
    Oct 7, 2017 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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .