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.

7
  • 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, 2014 at 16:14
  • Related: tex.stackexchange.com/questions/2132/… Jan 15, 2014 at 16:29
  • @StevenB.Segletes: But I think that the problem remains: how do I generate \argxiii from \i?
    – mort
    Jan 15, 2014 at 16:31
  • See my answer, and let me know if \whiledo is acceptable, or if you require \foreach. Jan 15, 2014 at 16:43
  • If you'd add something about what's your aim, it would be possible to tell more.
    – egreg
    Jan 15, 2014 at 16:47

4 Answers 4

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}
2
  • \whiledo is ok in my case. And this solution rocks!
    – mort
    Jan 15, 2014 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. Jan 15, 2014 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

0
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
  • 1
    Hm, would be simpler and nicer to just write \bar six times...
    – mort
    Jan 15, 2014 at 16:16

You must log in to answer this question.

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