8

How can I access the i'th argument of a \newcommand declaration via an iteration variable?

Assume I want a command \foo like:

\newcommand{\barr}[1]{argument 1 is: #1}

\newcommand{\foo}[6]{
    \foreach \i in {1,...,6}
        \barr{#\i}; %this does not work. \barr{#1}, \barr{#6} do work
}

EDIT: As requested a bit of background. I want a command like \foo{1}{2}{5}{0}{1}{2} to paint 1 ball in the first section, 2 in the second, 5 in the third and so on. Everything works fine, the only thing I was missing was to access the i'th parameter in a convenient way.

  • I'm not sure that I recognize which \foreach you use since there're more of them. Could you please convert your code into a Minimal (non-)Working Example? – yo' Jan 15 '14 at 16:14
  • @StevenB.Segletes: But I think that the problem remains: how do I generate \argxiii from \i? – mort Jan 15 '14 at 16:31
  • See my answer, and let me know if \whiledo is acceptable, or if you require \foreach. – Steven B. Segletes Jan 15 '14 at 16:43
  • If you'd add something about what's your aim, it would be possible to tell more. – egreg Jan 15 '14 at 16:47
4

This uses \whiledo instead of \foreach which may or may not be acceptable to the OP.

\documentclass{article}
\usepackage{stringstrings}
\newcounter{index}
\newcommand{\barr}[2]{argument #1 is: #2\par}
\newcommand\foo[1]{%
  \getargs{#1}%
  \setcounter{index}{0}%
  \whiledo{\theindex < \narg}{%
    \stepcounter{index}%
    \barr{\theindex}{\csname arg\romannumeral\theindex\endcsname}%
  }%
}
\begin{document}
\foo{A B C D EE F GGG H I JJJ K FinalArgument}
\end{document}
  • \whiledo is ok in my case. And this solution rocks! – mort Jan 15 '14 at 16:50
  • 1
    @mort The \getargs macro in stringstrings can be somewhat slow. A much faster version is \getargsC in the readarray package. So if you change the \usepackage and add a C to the macro name, speed will improve. – Steven B. Segletes Jan 15 '14 at 17:17
7

You can't specify a macro that way; the #1, #2 and so on must be literally present at definition's time, because they represent placeholders.


I don't see why limiting to six: you'll probably need the same for five or eight.

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\foo}{ O{,} m}
 {
  \mort_add_bar:nn { #1 } { #2 }
 }

\seq_new:N \l_mort_input_seq
\seq_new:N \l_mort_output_seq
\cs_new_protected:Npn \mort_add_bar:nn #1 #2
 {
  % split the input at the comma (or what's in the optional argument)
  \seq_set_split:Nnn \l_mort_input_seq { #1 } { #2 }
  % clear the output section
  \seq_clear:N \l_mort_output_seq
  % put each item inside \bar{...}
  \seq_map_inline:Nn \l_mort_input_seq
   {
    \seq_put_right:Nn \l_mort_output_seq { \bar{ ##1 } }
   }
  % output the sequence, separated by semicolons
  \seq_use:Nn \l_mort_output_seq { ; }
 }
\ExplSyntaxOff

\begin{document}
$\foo{a,b,c}$

% just to show that you can change the
% delimiter and have as many items as you wish
$\foo[.]{a.b.c.e.f.g.h.i}$ 
\end{document}

enter image description here

For drawing a number of objects, here's a possible solution:

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\foo}{ O{,} m }
 {
  \mort_add_bar:nn { #1 } { #2 }
 }

\seq_new:N \l_mort_input_seq
\cs_new_protected:Npn \mort_add_bar:nn #1 #2
 {
  \seq_set_split:Nnn \l_mort_input_seq { #1 } { #2 }
  \seq_map_inline:Nn \l_mort_input_seq
   {
    \mort_draw_balls:n { ##1 }
   }
 }
\cs_new_protected:Npn \mort_draw_balls:n #1
 {
  /
  \prg_replicate:nn { #1 } { \textbullet }
  /
 }

\ExplSyntaxOff

\begin{document}
\foo{1,2,5,0,1,2}

% just to show that you can change the
% delimiter and have as many items as you wish
\foo[-]{1-3-4}
\end{document}

enter image description here

4

egreg's and Steven B. Segletes's solutions are of course awesome. However, if one has a fixed number of arguments it seems a bit over the top.

As an alternative, this works too:

\newcommand{\bar}[1]{argument 1 is: #1}

\newcommand{\foo}[6]{
\foreach \i/\j in {1/#1,2/#2,3/#3,4/#4,5/#5,6/#6}   
    \putinbin{\i}{\j};
}
3

I don't think that TeX-core allows this in a simple way. The simplest way would be probably the following, but it's neither nice nor efficient since you have to put the correct number of terms (otherwise TeX-core complains about invalid parameter).

\newcommand{\bar}[1]{argument 1 is: #1}

\newcommand{\foo}[6]{
    \foreach \i in {1,...,6}
        \bar{\ifcase\i\or#1\or#2\or#3\or#4\or#5\or#6\fi};
}
  • 1
    Hm, would be simpler and nicer to just write \bar six times... – mort Jan 15 '14 at 16:16

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.