Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I suspect I'm asking for magic, but I'll go ahead anyway. Suppose I have a command that accepts an arbitrary-sized list of comma-separated terms. One that we're all familiar with is \usepackage although of course we could all come up with dozens more. In the case of \usepackage, we can write:

\usepackage[opta,optb,optc]{pkgx,pkgy,pkgz}

I.e.,

\usepackage[ <comma-separated-options> ]{ <comma-separated-pkg-names> }

More generally, we might be given a command to be used like this:

\command{ <comma-separated-list> }

My question is, is there any TeX\LaTeX wizardry for constructing commands such as:

\newcommand\mylist{
  ... possible wizardry ...
  a,b,c
  ... possibly more wizardry ...
}

so that the effect of writing

\command{\mylist} and \command{a,b,c}

or

\command{ ...code... \mylist ...code... } and \command{a,b,c}

would be the same?

In other words, is it possible to construct a TeX\LaTeX command that can stand, be expanded and accepted in the compile where <comma-separated-list> would normally be accepted?

Motivation: (apart from the immense pleasure of learning new TeX-related things :) ) On multiple occasions, I have to pass a large non-fixed-sized list to several places, some being to packaged commands that, due to their complexity and the maintenance burden it would impose, I'm rather reluctant to dive into and redefine.

Edit:

While some very good ideas have been suggested (thanks esp. TH and Will!), these solutions don't completely fit the bill. So, to ground my question somewhat, here are the specifics of the case I'm trying to solve. As background, my code consumes the same long list of values in multiple locations. While I could hard-code the list literally in all the places that consume it, for good and practical reasons, the idea of doing so is not at all appealing. I would really like to pass it as an argument, as I can for any other normal value. One of the (let's say) two places consuming the list is a command, \command, for which TH's and then Will's repeat, perfectly solve the problem. The other case where I'm struggling, however, is in my reusing the list in xkeyval command \define@choicekey, although the same problem would exist in the case of any other commands sharing the same type template. (FYI, I've tried hacking TH's nice solution to fit, but got nowhere particularly fast.) Here's an example list of values and the situations where I access them:

\newcommand*\mylist{a,b,c,...,l,m,n}

\define@choicekey{myfamily}{mykey}[]
  \mylist%             <-- how can I consume \mylist here?
  [a]
  {\renewcommand*\mynewkey{#1}}

\newcommand\anothercmd{
  ...
  \command{\mylist}%   <-- this problem is easily fixed with TH's nice solution
  ...
}
share|improve this question
    
Is the question how to define a new macro that expands its argument or how to ensure that the argument to an existing command is expanded? I answered the latter while Yiannis Lazarides gave a suggestion for the former. –  TH. Sep 3 '10 at 6:01
1  
@TH. The motivation makes it pretty clear he wants the latter. –  Charles Stewart Sep 3 '10 at 8:10
1  
That's what I thought, but he got an answer to the former as well, so I figured I'd ask in case I just misinterpreted it. –  TH. Sep 3 '10 at 8:31
add comment

6 Answers

up vote 4 down vote accepted

Well if \mylist is defined before the \define@choicekey it is rather easy to expand it before calling define@choicekey. If \mylist changes afterwards you will have to recall \define@choicekey and hope that xkeyval does all necessary cleaning to remove trace from the former content of \mylist.

\documentclass[]{article}

\usepackage{xkeyval}
\begin{document}
\makeatletter
\newcommand\mylist{left,center,right}
\toks@=\expandafter{\mylist}
\edef\next{\unexpanded{\define@choicekey*{fam}{align}[\val\nr]}{\the\toks@}
 \unexpanded{{%
  \ifcase\nr\relax
    raggedright
  \or
   centering
  \or
   raggedleft
 \fi}}}%
 \next

 \setkeys{fam}{align=left}
 \end{document}
share|improve this answer
    
Ulrike, I am most thoroughly impressed! Your code (or rather the ideas contained therein) does exactly what I had set out to achieve. It took a while to see how you had broken down and then reassembled the sub-terms, before the pattern you had produced just clicked. Discounting the superfluous elements, I can certainly say that it is a most elegant solution. Many, many, many thanks! –  Geoffrey Jones Sep 3 '10 at 14:03
    
This depends on e-TeX (probably a safe dependency) and it will only expand \mylist a single "level." Of course, that might be exactly what is desired. –  TH. Sep 3 '10 at 18:16
    
@TH, rather than using native \unexpanded, I've replace each of these in Ulrike's solution with \etex@unexpanded using HO's etexcmds package. Yes, single level expansion is all I need -- it hurts my brain just thinking about fine-controlling "multi-level" expansion in this way :)) –  Geoffrey Jones Sep 4 '10 at 1:05
    
Since you are already using \unexpanded, Ulrike, you could avoid using toks altogether, writing instead \unexpanded\expandafter{\mylist}. Also, I wouldn't include the last bit in the definition of \next, letting it be afterwards instead. But that's a matter of taste. –  Bruno Le Floch Aug 22 '11 at 18:14
add comment

Unless I'm mistaken, I think you're essentially asking for a new way to process keyval input. All of the keyval processing packages (keyval, xkeyval, kvoptions, l3keys, etc.) are very careful to ensure that the comma-separated entries they receive are not expanded.

It would be theoretically possible to have code like

\innerclist\mylist{x,y,z}

\processclist{a,\foo,b,\mylist,c,\bar,d} % etc.

only expand \mylist to insert x, y, and z into the processing but otherwise leave \foo and \bar alone, but keyval code requires a fair amount of care and attention to write.

An alternative might be to always fully expand your keyval arguments, if that is possible. If you just write

\edef\temp{a,\foo,b,\mylist,c,\bar,d}
\expandafter\processclist\expandafter{\temp}

then assuming \foo and \bar are expandable then you'll be okay. (This is the same as what TH wrote earlier.)

Finally, if you can restrict the position of your clist-to-be-expanded so that it only and always occurs at the beginning of your options, then you could simply use \expandafter on it, as so:

\def\innerclist{x,y,z}
\expandafter\processclist\expandafter{\innerclist,a,b,c,\foo}

The solution you choose (and what we can sensibly suggest) is pretty specific to the actual problems you're trying to solve.

share|improve this answer
1  
Will's quite right. All of the comma-list reading code (apart from that used for the \documentclass options) avoids any expansion as you never know what makes sense in a general list. –  Joseph Wright Sep 3 '10 at 7:04
add comment

The other answers are all very interesting in their own right, but the short answer to the question is simply No. You can't change the behaviour of \command solely by defining a special kind of \mylist.

share|improve this answer
    
Thanks, Taco, that's as definitive as can be and the answer I expected to receive. However, just for completeness, are you as definitive in your answer for the case of \command{ ...code... \mylist ...code... }, ie, inserting the invocation of \mylist within some expansionary and/or protective code? I suspect the answer in this case would be, "it all depends" (a la Will's and other answers), although I would be most interested to hear your thoughts. –  Geoffrey Jones Sep 3 '10 at 9:33
1  
It depends only on what \command does. If that expands its argument, then \mylist will work just fine. If it does not, then it will not. You can define \mylist in such a way that it does not expand when expansion is in fact asked for by the calling macro, but you can not define mylist such that it does expand when expansion is not asked for. –  Taco Hoekwater Sep 3 '10 at 11:12
    
Thanks Taco, that seems definitive enough. :) –  Geoffrey Jones Sep 3 '10 at 11:46
add comment

There's not really a way to do it in general. That said, you could easily write a macro that takes \command and {...code... \mylist ...code...} as arguments and expand to what you want.

\def\expandargument#1#2{%
    \edef\temp{#2}%
    \expandafter#1\expandafter{\temp}%
}
share|improve this answer
    
TH, while Ulrike's solution (suitably deconstructed), provides a general solution to my question, yours provides a very clean solution for the more limited, albeit, more common case. Many thanks, your code helped dig me out of a ditch. :) –  Geoffrey Jones Sep 3 '10 at 14:11
add comment

You can use the xkeyval package. It offers what you want and in addition you can define key value parameters as well.

share|improve this answer
    
xkeyval is a package for parsing options for use in implementations of new macros. The question asks how to interpolate the contents of (certain) macros into keyval lists passed to already existing macros. –  Charles Stewart Sep 3 '10 at 8:09
add comment

Here's a couple macros I wrote a year ago to do just that:

I start by defining the "main" macro, \ProcessList:

\newcommand\ProcessList[1]{\pr@cesslist#1,,}

This calls the supplementary macro \pr@cesslist:

\makeatletter % enables "@" to be used in macro names
\def\pr@cesslist#1,{%
    \ifx#1\empty%
    \else
        #1%
        \expandafter\pr@cesslist%
    \fi}
\makeatother

To illustrate how this works, let's run through the expansion of \ProcessList{\a,\b}, where \a and \b are some user-defined macros. LaTeX starts by expanding \ProcessList to \pr@cesslist \a,\b,,. Glancing at its definition, we find out that \pr@cesslist eats everything that follows it until it hits a comma. (Note, however, that the space immediately following \pr@cesslist in \pr@cesslist \a,\b,, is not eaten). Next, TeX checks to see whether \a is the empty string (\empty is defined by \def\empty{}); since it isn't, \a is expanded.

Now, recall that TeX is reads everything as a stream of tokens; right now, after \a has been expanded, that stream is \expandafter \pr@cesslist \fi \b , , plus whatever comes after. What we'd like to happen is for \pr@cesslist to eat \b; but without the \expandafter, TeX would feed it both \fi and \a (since they both appear before the first comma). With the \expandafter, TeX jumps past \pr@cesslist, expands \fi once (which is enough to get rid of it), then jumps back, leaving the stream as \pr@cesslist \b , , plus whatever comes after.

From there, it's simple tail-recursion.

EDIT: OK, so I've modified the above macro so that it won't expand the list entries until after the list is processed; e.g., if \abs is defined by \def\abs#1{|#1|}, then \ProcessList{\abs,a} will expand to |a|.

\makeatletter
\newcommand\ProcessList[1]{\newtoks\processedlist%
    \pr@cesslist\processedlist#1,,%
%   \showthe\processedlist         <---- this line is for debugging purposes
    \the\processedlist} % unpack \processlist
\def\pr@cesslist#1#2,{%
    \ifx#2\empty%
    \else
        \appendtoks{#2}{#1}% append #2 to token list #1
        \expandafter\pr@cesslist\expandafter#1%
    \fi}

\newcommand\appendtoks[2]{\toks0={#1}%
    \edef\act{\noexpand#2={\the#2\the\toks0}}
    \act}
\makeatother

The \appendtoks macro is, with some minor modifications, the \Prepend macro from page 154 of Eijkhout's TeX by Topic.

share|improve this answer
    
This does not work. You're creating a new token register every time you call \ProcessList. If you call it too many times, you'll run out of registers. You're not actually expanding the argument to the command which was what the question was about. If you trace the execution with your \abs and with \def\foo{a}, you see that you end up with \abs\foo in \processedlist and not \abs{a} which is what the OP wanted. –  TH. Sep 3 '10 at 5:59
    
How would I fix the token registers thing? Should I just use \toks0 instead of creating a new token register? As for the \abs\foo, that expands to \abs{a}. –  Avi Steiner Sep 4 '10 at 18:26
    
Put the newtoks call outside every macro. Once upon a time, \newtoks used to be "outer", i.e., impossible to put into the argument of any macro or definition. And it wasn't a bad idea. –  Bruno Le Floch Aug 22 '11 at 18:18
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.