4

Is it possible to pass an array argument to newcommand and then access those individual array elements?

For example, if I wanted to multiply the third element of the array (first argument) by the value of the second argument:

\newcommand{\hello}[2]{(#1.#3)*#2}

(Note #1.#3 is probably incorrect, I'm asking how to access this value.)

And using the command in the body code:

I want \hello{1,2,5}{2} gumdrops.

Edit: minimum working example?

\documentclass{standalone}
\newcommand{\hello}[2]{(#1)*(#2)}
\begin{document}
I want \hello{1}{2} gumdrops.
\end{document}

It's not really multiplying (I guess I don't know how to multiply numbers in newcommand), but that's not important to me and either way I want to be able to access the values of the arguments. Except in the first argument I'd like to be able to pass an array and access each specific element of it individually (i.e. not looping over it), which is totally not expressed in the MWE.

Hanmyo
  • 1,203
  • 1
    Yes. If your array argument implies using a comma-separated list, then you can iterate over the elements by following the guidelines in How to iterate over a comma separated list? – Werner Feb 17 '15 at 20:47
  • Thanks. I'm having a hard time reading that (I don't code much low-level LaTeX) but most of them don't seem to be accessing the actual individual elements and are just iterating over the list. I'm looking more to access the individual elements. The one thing I noticed was some ##1, which I'm not sure what that is but maybe that's what I want? – Hanmyo Feb 17 '15 at 20:55
  • Then you need to describe your use-case better. For example, \hello{A,2,c} creates a structure (an "array") with three elements, say. And you want to access \hello{1} as being the first element (returning A), \hello{2} returning 2 and \hello{3} returning c. Do you want to manipulate the elements after creating them as well, or just retrieve them? – Werner Feb 17 '15 at 21:03
  • I want to access them so that I can use them in calculations. Sorry I was unclear. I will change my question to be a bit more clear. – Hanmyo Feb 17 '15 at 21:14
  • I'm tempted to suggest a different user interface: \newarray{myarray}{1,2,3} and then use \getelement{myarray}{3}*2 or \getlastelement{myarray}*2. It would generalize your question since tomorrow you're going to be interested in performing some calculation on element 2, or some random element, for which "last element extraction" might not be general enough. – Werner Feb 17 '15 at 21:21
  • Does that mean that every time I want to call my hello command I need to explicitly create a new array? I just tried \newcommand{hello}{2}{\getelement{#1}{3}*#2} and it didn't work. – Hanmyo Feb 17 '15 at 21:31
  • Setting aside the array issues, your syntax for \newcommand is wrong. See this answer of mine for a simple introduction. – cfr Feb 18 '15 at 02:00
  • Please post a minimal non-working example (MnWE) which people can use as a basis for solutions, and indicate the output you want \hello{}{} to produce. (What should happen to the other elements in the array, for example? Note that LaTeX expects #1,...,#9 to refer to arguments 1,...,9. As such, you cannot specify a macro with 2 arguments and then use #3, for example, and you must tell LaTeX if your macro takes arguments and how many. This is explained a bit better in the answer I linked to. – cfr Feb 18 '15 at 02:06
  • @cfr thanks I fixed the syntax and provided a MWE I guess. And I meant #1.#3 to refer to the third element of the first argument. – Hanmyo Feb 18 '15 at 05:44
  • @cfr No LaTeX expect #1,...,#9 but TeX expect it. This is feature of TeX. And don't tell me that this is simplification. No, this is baffling the users. @Hanmyo If you are not able to explain your problem exactly then you cannot expect the solution. – wipet Feb 18 '15 at 08:38
  • @wipet LaTeX expects it because TeX expects it. But \newcommand is LaTeX. I have no idea why you think my comment baffling. – cfr Feb 18 '15 at 09:24

2 Answers2

2

The following might suit you:

enter image description here

\documentclass{article}
\usepackage{etoolbox,xparse}

\ExplSyntaxOn
  \cs_new_eq:NN \CALC \fp_eval:n
\ExplSyntaxOff

\newcounter{argcnt}
\makeatletter
\newcommand{\newarray}[2]{% \newarray{<array>}{<csv list>}
  \setcounter{argcnt}{0}% Restart argument count
  \renewcommand{\do}[1]{% With each element do...
    \stepcounter{argcnt}% Next arg
    \expandafter\gdef\csname #1@\theargcnt\endcsname{##1}% Store arg in \<array>@<num>
  }%
  \docsvlist{#2}% Store array
  \expandafter\xdef\csname #1@0\endcsname{\theargcnt}% Store array element count
}
\newcommand{\getelement}[2]{% \getelement{<array>}{<num>}
  \@ifundefined{#1@0}% <array> not defined
    {\@latex@error{Array #1 undefined}\@ehc}{}
  \expandafter\ifnum\csname #1@0\endcsname<#2 % <num> out of range
    \@latex@error{Array #1 only has \csname #1@0\endcsname\space elements}\@ehc
  \fi
  \csname #1@#2\endcsname% Print element
}
\newcommand{\calc}[1]{\expandafter\CALC\expandafter{#1}}% calculations
\makeatother

\begin{document}
\newarray{arrayA}{a,b,c}%
\newarray{arrayB}{1,2,3}%

\getelement{arrayA}{1}

$2 \times \getelement{arrayB}{2} + \getelement{arrayB}{3} = \calc{2*\getelement{arrayB}{2}+\getelement{arrayB}{3}}$
\end{document}

\newarray{<array>}{<csv list>} creates the array <array> with elements contained within the comma-separated value list <csv list>. \getelement{<array>}{<num>} acts like an array indexing, while \calc can be used to perform calculations on numeric elements.

The approach allows you to store virtually anything in the array, rather than just numbers (and/or mix them).

Werner
  • 603,163
1

Is this something like what you mean? Perhaps this could get you started.

\documentclass{article}

\newcommand{\twothings}[2]{%
    \gdef\thingone{#1}
    \gdef\thingtwo{#2}
}

\newcommand{\pickone}[1]{%
    \ifnum#1 = 1
        \thingone
    \else
        \ifnum#1 = 2
            \thingtwo
        \else
            \relax
        \fi
    \fi%
}

\begin{document}

\twothings{First}{Second}

The second thing is \pickone{2}.

\end{document}
musarithmia
  • 12,463
  • Sorry, that's not really what I'm looking for. I think you meant \pickone{1}{2} in your code, but I really wanted to pass an array/list type argument and grab the elements. This one just passes two separate elements and looks at them. – Hanmyo Feb 18 '15 at 08:21
  • Actually I meant \newcommand{\pickone}[1] and then #1 for the arguments in the code, since there was only one argument. I edited the answer. – musarithmia Feb 18 '15 at 12:22
  • In TeX a control sequence expands to a single definition (e.g., \def\array{data}), so there are not multiple elements to access. Instead you have to make an array manually by creating a series of control sequences with sequential keys in their names (using an \expandafter trick you define \array1{data1}, \array2{data2}, etc.). Then you can access the contents of specific "array elements" by calling the control sequence you desire (\array2). @Werner shows a good way to do this. – musarithmia Feb 18 '15 at 12:26
  • Just to be clear about arguments, in TeX you define a command that takes multiple arguments like this: \def\command #1#2{#1#2}. In LaTeX you do this: \newcommand{\command}[2]{#1#2}. In both examples \command expands to a string that is the combination of both arguments passed to it, so \command{book}{keeping} is typeset as bookkeeping. In normal commands there is no way to access the arguments separately from the command. In my answer I showed how to make a command that defined two control sequences which you could then access separately. This is like a miniature array, I suppose. – musarithmia Feb 18 '15 at 12:31