This question was inspired by comment banter in this question. See title.
8 Answers
(Sorry, this gets more of an essay than an answer.)
TH claims that "There's not really a good way to do this that takes arguments." Well, I thought the same, but Philippe's answer to the original question of Yiannis proved me wrong. I've used his ingenious \csname-idea for a \multexpafter command that takes the number of expansions as an argument. New: I've implemented a much more efficient version that allows 0-100 expansions.
The crucial feature of the \multexpafter command is that it always needs 3 steps to be fully expanded. (I believe less steps to be impossible, but feel free to prove me wrong; see above :-)) This is in contrast with 2^n-1 consecutive \expandafters that need n expansion steps. I want the following TeX code to work (compare the "Hello world" example in my answer to Yiannis question):
\def\a#1.{#1}
\def\b#1:{#1.}
\def\c#1,{#1:}
\def\d{Hello world!,}
\multexpafter4\a
\multexpafter4\b
\multexpafter1\c
\d
Explanation, starting in the end: The \multexpafter1 should expand the \d after \c once. This should happen before the expansion of \b, so the \multexpafter4 in front of \b expands the \multexpafter1... 4 times. Three of these expansion steps are needed to fully expand the \multexpafter1, the last steps then expands the \c once. Same for the \multexpafter4 in front of \a: It expands the \multexpafter4\b... 4 times; the last of these steps expands the \b once. More generally, use
\multexpafter{<3+n>}\a
\multexpafter{<m>}\b\c
to first expand \c m times and then \b n times before expansion of \a. Again, 3 of the 3+n expansion steps are needed to fully expand \multexpafter{<m>}; then the \multexpafter{<m>} is gone, and so the next n expansion steps act on the \b.
Here's the new implementation of \multexpafter (to be compiled with tex or pdftex). It uses e-TeX's \numexpr (which to me feels a bit like cheating):
\catcode`@=11
\let\expandafter@i\expandafter
\count255=1
\loop\ifnum\count255<200
\edef\temp{\the\count255}
\advance\count255 by 1
\expandafter\def\csname expandafter@\romannumeral\count255\expandafter\endcsname
\expandafter{\expandafter\expandafter
\csname expandafter@\romannumeral\temp\endcsname \expandafter}%
\repeat
\def\expandafter@{}
\def\expandafter@e{\errmessage{Argument of \noexpand\multexpafter must be between 0 and 100}}
\def\multexpafter#1{\csname expandafter@\ifnum#1>100 e\else
\csname expandafter@\ifnum#1=0 ii\else\romannumeral\numexpr2*#1\fi\fi\endcsname
\endcsname\csname expandafter@\romannumeral#1\endcsname}
\catcode`@=12
\def\a#1.{#1}
\def\b#1:{#1.}
\def\c#1,{#1:}
\def\d{Hello world!,}
\multexpafter4\a
\multexpafter4\b
\multexpafter1\c
\d
\bye
Only a short explanation: In the loop I construct macros \expandafter@ii to \expandafter@cc for expanding up to 200 times. The 3 expansion steps of \multexpafter{<n>} are as follows: The first step yields the replacement text of the macro. In the second step the \csname is executed, which triggers
\csname expandafter@\romannumeral<n>\endcsname
to be expanded 2n times, which in turn executes the desired n expansions. The result of the second step is just the control sequence \expandafter@, which in the third step expands to nothing.
For further illustration, here's how Philippe's example looks like with \multexpafter:
\def\a{a}
\def\b{b}
\def\c{\C}\def\C{c}
\def\d{d}
\def\e{\E}\def\E{\ee}\def\ee{e}
\multexpafter3\def
\multexpafter3\temp
\multexpafter4{%
\multexpafter4\a
\multexpafter5\b
\multexpafter4\c
\multexpafter3\d
\e
}
\show\temp
For completeness, here's my old inefficient implementation for 0-6 expansions:
\catcode`@=11
\def\expandafter@one{\expandafter}
\def\expandafter@two{\expandafter\expandafter\expandafter}
\def\expandafter@three{\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter}
\def\expandafter@four{\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter}
\def\expandafter@five{\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter}
\def\expandafter@six{\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter}
\def\do@nothing{}
\def\do@nothing@{\errmessage{Argument of \noexpand\multexpafter must be between 0 and 6}}
\def\m@e@a#1{\ifcase#1
\expandafter\do@nothing \or
\expandafter\expandafter@one \or
\expandafter\expandafter@two \or
\expandafter\expandafter@three \or
\expandafter\expandafter@four \or
\expandafter\expandafter@five \or
\expandafter\expandafter@six \else
\expandafter @\fi}
\def\multexpafter#1{\csname do@nothing\expandafter@six\m@e@a
\expandafter@five#1\expandafter@four\endcsname\m@e@a#1}
\catcode`@=12
- 37,935
-
Ha! 395
\expandafters executed for the hello world example. That's amazing. – TH. Jan 02 '11 at 12:57 -
For the record, mine uses 39 and the minimum number is 11:
\ea\ea\ea\ea\ea\ea\ea\a\ea\ea\ea\b\ea\c\d. I wish TeX had a way to define expandable macros that were always expanded all the way. Something analogous to e-TeX's\protectedprefix. Maybe an\expandedprefix:\expanded\def\foo{\bar}and then\expandafter\a\foowould be expanded like it would when "building an expanded token list (for\edef,\xdef,\message,\errmessage,\special,\mark,\marks, or when writing the token list for\writeto a file), The e-TeX manual. – TH. Jan 02 '11 at 13:11 -
@TH.: Well, I didn't say it was efficient. But it is easier to use, isn't it? (The
\expandedprefix sounds like an interesting idea indeed.) – Hendrik Vogt Jan 02 '11 at 13:53 -
I'm uncomfortable saying that either are easy to use. E.g., why is it
\mea4\a\mea4\b\mea1\c\d? That is, why 4, 4, 1? What would it be if you wanted to reverse the expansion order of\a\b\c\d\e? 4, 4, 4, 1? I guess that's it. I'm starting to see how this works. Once you get to expanding the\csname ... \endcsname, you get complete expansion. I wonder if this can be simplified using Joseph's\romannumeraltrick. – TH. Jan 02 '11 at 21:47 -
Joseph mentionned somewhere else the etextools package. Among many other things, it defines an expandable
\expandnextthat allows you to do things like\expandnext{\def\test}{\csname name\endcsname}for\def\test{\name}. I will need to study this package to understand the details. It seems very powerful! – Bruno Le Floch Jan 02 '11 at 23:21 -
@Bruno: I've used
etoolboxa lot (since I learned about it). I hadn't seenetextoolswhich is an extension. Thanks! – TH. Jan 03 '11 at 03:21 -
@Bruno: The
\showcsmacro alone is fantastic. I don't know how many times I've written\show\fooonly to find that it was one of the LaTeX protected macros and then written\expandafter\show\csname foo \endcsname. – TH. Jan 03 '11 at 03:32 -
@TH.: I still claim it's easier, so maybe I should improve my explanation. But you got it, I think. You need
\mea<3+n>to fully expand the next\meaand to expand the stuff after thatntimes. For the record,\seaxi\a\seaix\b\seavii\c\seav\d\seaiii\e\seai\f\guses 2724\expandafters,\mea4\a\mea4\b\mea4\c\mea4\d\mea4\e\mea1\f\guses "only" 818. It should be possible to improve that substantially. By the way, there's a double "optional minus" in your blog post, where one of those should be "optional space". – Hendrik Vogt Jan 03 '11 at 13:07 -
@Hendrik: Oh yes, I meant to actually calculate how many get used for both. It seems clear that yours increases linearly (with a fairly large coefficient). Just using
\expandaftertakes 2^(n+1)-n-2. Mine is far, far worse. It takes (2^(2n+1)-3n-2)/3, assuming I calculated those correctly. So yeah, now that I understand what's going on with yours, it is easier. Thanks for pointing out the blog post typo. – TH. Jan 03 '11 at 18:44 -
@TH.: I managed to get it down from 395 to 60. Moreover, it works up to
\mea{99}now, and execution of\mea{<n>}only produces 6n+2\expandafters in total. So also this part only grows linearly. Not bad, eh? I'm just not sure if I should post the almost incomprehensible code. – Hendrik Vogt Jan 03 '11 at 19:18 -
-
1@TH.: I'm down to 48, or 6n-2 for
\mea{<n>}, with much shorter code, but using\numexpr. I'm ready to post it soon. – Hendrik Vogt Jan 04 '11 at 14:55 -
@TH.: I'm still wondering if one can use
\romannumeralso that\multexpafterneeds only 2 expansion steps. One idea is\romannumeral-\X\csname expandafter@\romannumeral#1\endcsname`, but how do I stop the expansion process? Do you have any ideas? – Hendrik Vogt Jan 04 '11 at 17:19 -
@Hendrik: I'll have to take a closer look when I go home, but one way to stop the expansion is to give a space token when you're done. – TH. Jan 04 '11 at 20:26
-
@TH. @Hendrik: you could be interested in my answer below, which does things in two steps. It can be adapted to partially answer TH.'s wish for automatically expanding macros: it is then possible to build macros that fully expand after two steps of expansion. – Bruno Le Floch Feb 11 '11 at 19:50
-
@Hendrik: as you mention below, I ended up using this idea of yours (
\romannumeral) instead of\unless\ifcsname. To stop it, I use a space:\romannumeral0\expandafter\spacewill expand the next token once before stopping\romannumeralsafely. – Bruno Le Floch Feb 16 '11 at 17:50 -
@Bruno: Ah, OK, that does the trick!
\romannumeralis also used a lot inetextools, but I don't know if the stopping trick is used, too. – Hendrik Vogt Feb 16 '11 at 21:47
(I apologize for the length of this answer.)
There's not really a good way to do this that takes arguments. There are two reasons for this. The first is that it adds more tokens that have to be skipped over. For example, consider \sea{3}. If you need to skip over this, there's no simple way to get over those three tokens, at least not using another \sea. Even if you omit the braces, there's still one extra token.
The second reason has to do with implementing this. Consider
\let\ea\expandafter
\def\sea#1{%
\ifcase#1
\or \ea
\or \ea\ea\ea
\or \ea\ea\ea\ea\ea\ea\ea
\fi
}
This is the obvious first choice, but that doesn't really work; consider \sea1. It'll expand to the \ifcase ... \fi and then if the \ifcase is expanded next, it'll expand to \ea\or \ea\ea\ea...\fi and then if the \ea is next, it'll skip the \or, start expanding a whole series of other \ea. To deal with this, you have to add yet more \eas in the definition to make it to the next \or or the \fi. It quickly becomes totally unworkable.
You can sort of use
\let\ea\expandafter
\def\seai{%
\ea
}
\def\seaii{%
\ea\ea\ea
}
\def\seaiii{%
\ea\ea\ea\ea\ea\ea\ea
}
\def\seaiv{%
\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea
}
\def\seav{%
\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea
\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea\ea
}
But it's not extremely easy to use or follow.
\def\a{A}
\def\b{B}
\def\c{C}
\def\d{D}
\tracingall
\seai\a\b
\seaii\a\seai\b\c
\seaiii\a\seai\b\c
\seaiv\a\seaiii\b\seai\c\d
\seav\a\seaiii\b\seai\c\d
\bye
The first line expands \b and then \a. The second line expands \c, \a, then \b. The third line expands \c, \b, \a. The third expands \d, \c, \a, then \b. The fourth expands \d, \c, \b, \a. You can verify this by reading the log.
This can be simplified somewhat by not using \seai and just using \ea because then it doesn't have to be expanded twice to have the effect.
Edit:
After having played with this a bit. A pattern emerges. Using appropriate definitions, you can use
\seavii\a\seav\b\seaiii\c\seai\d\e
to expand \e, \d, \c, \b, and finally \a. Using \seavi instead of \seavii swaps the order of \a and \b. Other expansion orders are possible by changing the values, but it's not immediately obvious (at least it isn't to me) what a particular order will do.
For example, guess what
\seavi\a\seaiv\b\seaii\c\seai\d\e
will do.
Edit 2:
It's possible to get TeX to create the various \seaX macros for us rather than the error-prone manual method above.
\def\seai{\expandafter}
\count255 1
\loop\ifnum\count255<20
\expandafter\let\expandafter\temp\csname
sea\romannumeral\count255\endcsname
\advance\count255 1
\expandafter\gdef\csname sea\romannumeral\count255\expandafter
\expandafter\expandafter\endcsname\expandafter\expandafter
\expandafter{\expandafter\temp\temp\expandafter}%
\repeat
- 62,639
[Again, not really answering the question but posing an alternative.]
Nowadays, rather than attempting to repeat \expandafter in unreadable (and unmaintainable) chains, I recommend using the expl3 programming environment to deal with complex expansion problems.
Here's an example:
\exp_args:Nooo \foo \a \b \c
Here, \foo is not expanded (the N in the exp_args command) and \a, \b, \c are all expanded once (the o letters). But this isn't exactly how expl3 is intended to be used; instead, one would define that foo takes, say, five arguments by first defining \foo:nnnnn and then creating a variant for which each argument would be expanded once before execution of the function:
\usepackage{expl3}
\ExplSyntaxOn
\cs_set:Nn \foo:nnnnn { ...#1...#3...#5... }
\cs_generate_variant:Nn \foo:nnnnn {ooooo}
...
\foo:ooooo \a \b \c \d \e
...
\ExplSyntaxOff
As well as o (for ‘expand once’) there are other argument specifiers such as V (value of macro or register), c (create a csname), x (complete expansion), f (‘romannumeral’ expansion), and so on. Using this system allows programmers to bypass many of the problems involved with expansion in classical TeX programming.
- 73,872
-
Of course, behind the scenes the code does construct the appropriate chain of primitives :-) – Joseph Wright Jan 05 '11 at 07:01
-
This is a nice feature of expl3 indeed, but what happens if the arguments of
\foo:oooootake arguments themselves, such as in my stupid "Hello world" example? For me it's not straightforward how to do that in expl3. – Hendrik Vogt Jan 05 '11 at 09:55 -
@Hendrik In practise we've found that it's generally not very common at all to come across situations like this — and when it does happen, it possibly indicates that your code is too convoluted and would be more readable if split up
:)(although sometimes that's impossible when you need an expandable solution). We're not saying that expl3 can do everything; its goal is to make common things easier by providing wrappers around patterns in common TeX programming that often require ‘tricks’. – Will Robertson Jan 06 '11 at 01:03
EDIT2: the previous code was broken when given conditional text as an input. I think this should be better. Also, the explanations were confusing. Now, for all the explanations below I
\let\ea\expandafter. But not in the code.
TOC: Explanations ("Two user commands", "How it works"). Code ("Implementation", "Tests").
Explanations
Two user commands
For n>0,
\MultiExpand{n}\macrogives the n-th expansion of\macroafter two steps of expansion.For n>0,
\MultiExpandAfter{10}\macroA\MultiExpandAfter{4}\macroB\macroCexpands\macroC4 times before expanding\macroB8=10-2 times, and finally\macroA. It also requires two steps.
The first expansion of \MultiExpand yields a very useful sequence of tokens: expanding \unless\ifcsname\multiexpand\fi{n} once expands the following token n times. The same exists for expanding after.
These are especially useful when we want to expand several times a very specific token which is buried behind many others. Example: after \def\macroA{\macroY\macroZ}, the code
\MultiExpand{5}\expandafter\macroA\expandafter\macroB%
\unless\ifcsname\multiexpandafter\fi{4}\macroC\macroD
will expand \macroD 4 times, then will expand \macroA 4=5-1 additional times. Also, these tokens can be used to force expansion of an expandable macro, more or less like TH. was asking for: just prepend it the macro with the relevant few tokens.
Note: if you really need \MultiExpand{0}, just do \empty\empty.
How it works
\unless\ifcsname expands tokens fully until it reaches \endcsname. If the control sequence thus built exists, then it jumps to the matching \fi and eats it. All this happens in one step: try \ea\def\ea\foo\ea{\unless\ifcsname let\endcsname \fi}. Since \let exists, \foo becomes empty. Try removing \unless. Then the conditional becomes true, and the \fi remains: the full expansion would require two steps. We will always arrange for this conditional to be false, to get rid of the \fi. Since it is much easier to ensure that a command exists than not, we use \unless\ifcsname ifcsname\endcsname\fi.
Now, as I said before, \ifcsname expands tokens. The trick is to put the various tokens that we want to expand between the \ifcsname and the matching \endcsname. The simplest example is \unless\ifcsname\endifcsname:. In one step, it expands to the empty token list, and additionally expands its argument once. If you think about it, (almost) everything is just as if these three tokens were not there.
This is in fact how the construction ends. Let us quickly look at the definition of \multiexpand:n, whose argument k is the number of expansion left to do. If k=1, we end the \ifcsname as described in the paragraph above, throwing away two last lines in braces (I hide this fact at the end of the macro name \endifcsname:n). Otherwise, we keep the two lines in braces: they do one step of expansion (\numexpr#1-1\expandafter), and leave \multiexpand:n{k-1} on the stream. It will be expanded, since we are still inside the construction of the cs name for \ifcsname.
The code
Implementation
% I follow the LaTeX3 convention of finishing each macro name by its
% argument specification, e.g. ":nnnN" for three braced arguments and
% one single token. For this, I need "_" and ":" to be letters.
\catcode`\:=11\relax%
\catcode`\_=11\relax%
\def\use:n#1{#1}%
\def\endifcsname:{ifcsname\expandafter\endcsname\expandafter\fi}%
\def\endifcsname:n#1{\endifcsname:}%
\def\endifcsnameafter:n#1{\endifcsname:\expandafter}%
%
% The user commands are \MultiExpand and \MultiExpandAfter.
\def\MultiExpand{\unless\ifcsname\multiexpand\fi}%
\def\multiexpand\fi{\multiexpand:n}%
\def\multiexpand:n#1{%
\ifnum#1<2 \expandafter \endifcsname:n%
\else \expandafter \use:n%
\fi%
{\expandafter \multiexpand:n \expandafter {%
\number\numexpr#1-1\expandafter}}%
}%
% Almost identical definitions for expanding after...
\def\MultiExpandAfter{\unless\ifcsname\multiexpandafter\fi}%
\def\multiexpandafter\fi{\multiexpandafter:n}%
\def\multiexpandafter:n#1{%
\ifnum#1<2 \expandafter\endifcsnameafter:n%
\else \expandafter \use:n%
\fi%
{\expandafter \multiexpandafter:n \expandafter {%
\number\numexpr#1-1\expandafter}\expandafter}%
}%
\catcode`\_=8\relax%
\catcode`\:=12\relax%
Tests
There is no limit on the number of expansions.
% ======= Tests =====
\long\gdef\expandonce#1{% redefines #1 as #1 expanded once.
\long\edef#1{\unexpanded\expandafter\expandafter\expandafter{#1}}}
% Commands that expand to each other, to count how many times things
% were expanded.
\def\0{\1} \def\1{\2} \def\2{\3} \def\3{\4} \def\4{\5}
\def\5{\6} \def\6{\7} \def\7{\8} \def\8{\9} \def\9{\0x}
% Test \MultiExpand, after two expansions,
% we get the 2011th expansion of \0.
\def\:{\MultiExpand{2011}\0} \show\:
\expandonce\: \show\:
\expandonce\: \show\:
% Test \MultiExpandAfter, two expansions to get the specified expansion.
% The results also show that the expansion happens in the expected order.
\def\:{%
\MultiExpandAfter{27}\0% note that we get \5xx (25=27-2).
\MultiExpandAfter{14}\0% note that we get \2x (12=14-2).
\MultiExpandAfter{38}\0\0}% note that we get \8xxx... no "-2".
\show\:
\expandonce\: \show\:
\expandonce\: \show\:
For comparison, the "Hello World!" example
\def\a#1.{#1}
\def\b#1:{#1.}
\def\c#1,{#1:}
\def\d{Hello world!,}
\MultiExpandAfter3\a
\MultiExpandAfter3\b
\MultiExpandAfter1\c
\d
now uses 32 \expandafters, slightly better than Hendrik's solution (however, we should count the overhead with all the \csname and friends). The other example on which Hendrik was optimizing, namely, expanding seven tokens once each in the reverse order, takes 74 \expandafter:
\def\a{a} \def\b{b} \def\c{c} \def\d{d}
\def\e{e} \def\f{f} \def\g{g}
\begingroup\tracingall
\MultiExpandAfter3\a \MultiExpandAfter3\b
\MultiExpandAfter3\c \MultiExpandAfter3\d
\MultiExpandAfter3\e \MultiExpandAfter1\f\g
\endgroup
Some experimentation tells me that the number of \expandafter is roughly 5 times the sum of the arguments of the various \MultiExpandAfter.
- 44,937
-
@Bruno: Very nice, though I'm only slowly understanding it. A question and two comments: 1. The trick is that
\unless\ifcsname ifcsname\endcsname\else\blubb\figives the full expansion of\blubbin one step, correct? 2. "expanding seven tokens once each": If you were alluding to Philippe's example, then it's 0 to 3 expansions. 3. In the comments to this answer of Philippe, Philippe asked if it would be worth putting this in a package. (Then it should be your version.) – Hendrik Vogt Feb 13 '11 at 17:00 -
@Hendrik: I am rewriting my answer above. 1. No, see the update above in an hour or so. 2. I was alluding to a comment of yours: "For the record,
\seaxi\a\seaix\b\seavii\c\seav\d\seaiii\e\seai\f\guses 2724\expandafters,\mea4\a\mea4\b\mea4\c\mea4\d\mea4\e\mea1\f\guses 818." I don't quite understand how many times each token\a,...\gis expanded in that example. 3. I will make it into a package some time next week, depending on where the Uppercase discussion on LaTeX-L goes. – Bruno Le Floch Feb 13 '11 at 18:22 -
@Bruno: 2. Ah, OK, now I got it. Thanks! Of course, in that example each token is expanded once. – Hendrik Vogt Feb 13 '11 at 18:26
-
@Bruno: You saw, no HTML, only markdown in comments. Use
**for bold. Moreover, inline code mustn't be followed by a letter in comments:-(In answers it works; I guess you want\expandafters (without the space) at least twice in your answer. – Hendrik Vogt Feb 13 '11 at 18:30 -
@Hendrik: I updated my answer. As far as I can tell, the number of
\expandaftergrows linearly, with a reasonably small constant. Thanks for the**tip. How do you do short links (e.g. http://tex.stackexchange.com writes the full address)? – Bruno Le Floch Feb 13 '11 at 18:46 -
@Bruno: Yes, it goes linearly, with 5n-1
\expandafters (I needed 6n-2). For short links:[text](http://...). I guess you still want\expandafters (without the space) at least twice in your answer, as I mentioned above:-)– Hendrik Vogt Feb 13 '11 at 18:55 -
@Hendrik: I'm seeing only one "
\expandafters", so I won't edit just for this:). Can you take a look at the package I put online? I will submit it to CTAN soon, and I would be happy to mention you at the place where I talk about the{TeX}posts that were useful. – Bruno Le Floch Feb 15 '11 at 21:43 -
The multiexpand macros coincidentally and interestingly look similar to
\expandtimesof Ulrich Diez. You can contact her on CTT. – Ahmed Musa Feb 16 '11 at 02:49 -
@Bruno: Ah, I only now see that your package has yet another improved version with
\romannumeral. This answer a question in one of my comments above. I haven't had a close look so far. – Hendrik Vogt Feb 16 '11 at 17:26 -
@Bruno: Are you sure you want all those
%signs at the line ends? After a cursory glance I'd say they're only needed after{and}. (But the code is becoming more and more elegant!) – Hendrik Vogt Feb 16 '11 at 22:02 -
@Hendrik: most of the % are indeed irrelevant, but I tend to make mistakes (and spaces are quite relevant here) :). – Bruno Le Floch Feb 17 '11 at 00:57
-
@Bruno: Now I have so many comments that I'll try and send you an e-mail. – Hendrik Vogt Feb 17 '11 at 14:19
-
@Bruno: Did you already have a look at Ulrich Diez'
\expandtimes? Here's a link with tons of code. – Hendrik Vogt Feb 18 '11 at 10:27
We first give a direct answer to the question. Later we give another, in our opinion much nicer, way to reverse the expansion of tokens.
The direct answer is given by the following code. In the first macro we make an expandable countdown counter which is used in the macro \expand, which expands to itself with a doubled argument (#2#2) with a decreased counter (\number\BSp@decnumber{#1})
\def\BSp@decnumber#1{%
\ifcase#1\or0\or1\or2\or3\or4\or5\or6\or7\or8\or9\or10
\or11\or12\or13\or14\or15\or16\or17\or18\fi}
\def\expandafterN#1{\romannumeral-`X\expand{#1}\expandafter}
\def\expand#1#2{\ifnum#1=\z@\putafterfi{ }\else
\putafterfi{\expandafter\expand
\expandafter{\number\BSp@decnumber{#1}}{#2#2}#2}\fi}
\def\putafterfi#1#2\fi{\fi#1}
In this definition we use a trick described in Forcing full expansion. An expansion of \expandafterN{n} gives after two expansion 2^n-1 \expandafter's (the answer to the question).
Now we define some macros to show the order of expansion (see also the answer by Hendrik Vogt of Jan 2, 2011)
\def\a#1.{#1}
\def\b#1:{#1.}
\def\c#1,{#1:}
\def\d{Hello world,}
\edef\aaa{\expandafterN{10}\a\expandafterN6\b\expandafterN2\c\expandafter\d\e}
\show\aaa
This gives in the output window
> \aaa=macro:
->Hello world.
l.153 \show \aaa
exactly what we want. Note that for every extra token that we want to expand in reversed order, we need to increase the counter by 4. This seems not to much, but essentially, at every step the number of \expandafter's is multiplied by 16.
For another way to reverse the order of expansion we use the same trick, but now directly. Note that for reversing the order of expansion of n tokens, we only need 4(n-1) \expandafter's and (n-1) \romannumeral's.
\edef\aaa{\romannumeral-`X\expandafter\expandafter\expandafter\space\expandafter\a
\romannumeral-`X\expandafter\expandafter\expandafter\space\expandafter\b
\romannumeral-`X\expandafter\expandafter\expandafter\space\expandafter\c
\romannumeral-`X\expandafter\expandafter\expandafter\space\expandafter\d
\e}%
which again gives the desired result. As inserting the tokens
\romannumeral-`X\expandafter\expandafter\expandafter\space\expandafter
may obscure the TeX code, we can at the cost of more (namely 2n-5)) \expandafter's define the macro
\def\RNexpandafter{%
\romannumeral-`X\expandafter\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter\space
\expandafter\expandafter\expandafter}%
\edef\aaa{\RNexpandafter\a\RNexpandafterRN\b\RNexpandafter\c\expandafter\d\e}
which again gives the desired result.
- 151
- 2
- 2
-
\romannumeral-`Xalso gobbles a space, thus\romannumeral-`X\expandafter\foowill expand\foountil it gobbles a space or meets an unexpandable token. Thus something like\romannumeral-`X\expandafter\space\expandafter\foois needed. (`means the grave accent U+0060.) – Heiko Oberdiek Aug 18 '13 at 16:24 -
@Heiko Thanks for pointing out the error. The code is adjusted according to the comment. – Jan-Kees Aug 18 '13 at 18:52
-
If not wanting to remove a space, doing
\romannumeral\expandafter\stopromannumeralwith\stopromannumeralbeing adenoting 0 instead of – Ulrich Diez Feb 27 '24 at 11:02\romannumeral-`\expandafter\space\expandafter\foois shorter.
In OpTeX, we can use \fornum 1..\expr[0]{2^#1-1}:
\def\superexpandafter#1{%
\expanded{%
\immediateassignment\def\tmp{}%
\fornum 1..\expr[0]{2^#1-1}\do{\immediateassignment\addto\tmp{\ea}}%
}\tmp
}
%% Tests:
\def\X{\Y} \def\Y{\Z} \def\Z{?}
\def\test#1{\wterm{\unexpanded{parameter is #1}}}
\superexpandafter{0}\test\X % prints: parameter is \X
\superexpandafter{1}\test\X % prints: parameter is \Y
\superexpandafter{2}\test\X % prints: parameter is \Z
\superexpandafter{3}\test\X % prints: parameter is ?
%% this is equivalent to:
\test\X % prints: parameter is \X
\ea \test\X % prints: parameter is \Y
\ea\ea\ea \test\X % prints: parameter is \Z
\ea\ea\ea\ea\ea\ea\ea \test\X % prints: parameter is ?
\bye
Another solution runs loop only from 1 to n and doubles the number of \ea in each loop step. We have 2^n \eas in the resulting \tmp macro, so we remove one \ea using \ignoreit.
\def\superexpandafter#1{%
\expanded{%
\immediateassignment\def\tmp{\ea}%
\fornum 1..#1\do{\immediateassignment\edef\tmp{\unexpanded\ea\ea\ea{\ea\tmp\tmp}}}%
}\ea\ignoreit\tmp
}
- 74,238
n in your question seems to denote two different things:
- A variable within the mathematical expression 2n - 1. The value domain of that variable is not specified.
- A constellation of tokens which forms the argument of the macro-token \superexpandafter.
With the following considerations the focus is on 1., i.e., on n denoting a variable within a mathematical expression.
As you wish to obtain an amount of tokens \expandafter, the mathematical expression 2n - 1 is to represent a number of the kind "number of pieces", i.e., a non-negative integer.
There are also non-integer values for n, where the expression 2n - 1 represents a non-negative integer. Nevertheless, for the sake of avoiding things like calculating binary logarithms by means of TeX, i.e., for the sake of making maths and programming and thus life easier, I assume that the value domain of n is formed by a closed interval of integers whose lower limit is 0. ;-)
If n is large, then 2n - 1 \expandafter are a lot of \expandafter.
For the thing to work out, these need to fit into TeX's memory.
First let's remember some complicated ;-) maths:
Fact 1: 2(0) - 1 = 0.
Fact 2: (2(k) - 1) · 2 + 1 = 2(k+1) - 1.
I.e., you can get 2n - 1 tokens \expandafter by recursively
doubling the amount of already accumulated \expandafter and adding an \expandafter.
Begin the recursion with having already accumulated 0 tokens \expandafter (which is 2(k) - 1 tokens \expandafter for the case k = 0) and do the recursion n times.
You can use \romannumeral for this.
In the following example, with two "hits" by \expandafter, the expansion-cascade coming from the macro \superexpandafter{⟨number denoting the value n⟩} produces 2n - 1 tokens \expandafter.
If you want these \expandafter-tokens in a way, where the expansion-cascade works out with just one "hit" by \expandafter, then use \romannumeral\startsuperexpandafterloop{⟨number denoting the value n⟩}, where \romannumeral needs to be "hit" by one \expandafter for the expansion-cascade to produce the desired 2n - 1 tokens \expandafter.
In any case, if using \superexpandafter{⟨number denoting the value n⟩} or \romannumeral\startsuperexpandafterloop{⟨number denoting the value n⟩}, then ensure that in unusual circumstanced with non-standard \lccode-régime and/or non-standard \uccode-régime things like \uppercase or \lowercase won't interfere before the \expandafter-tokens are delivered—these primitives might turn ⟨digit⟩-tokens into non-⟨digit⟩-tokens and this way distort the process of gathering the tokens that belong to the ⟨number denoting the value n⟩. Or even worse—e.g., after \uccode`\1 =`\9 % \uppercase{1} turns 1 into 9 … ;-)
%%----------------------------------------------------------------------
%% \romannumeral\startsuperexpandafterloop{<number n>}%
%% -> delivers 2^n-1 tokens "\expandafter".
%%
%% n is a non-negative integer.
%% <number n> is a TeX <number> which represents the value of
%% the non-negative integer n.
%%----------------------------------------------------------------------
\chardef\stopromannumeral=`\^^00 %
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\def\innerdfork#1d#2#3dd{#2}%
\def\dfork#1{\innerdfork#1{\firstoftwo}d{\secondoftwo}dd}%
\def\startsuperexpandafterloop#1{%
\expandafter\superexpandafterloop\expandafter{\expandafter}%
\romannumeral\number\number#1 000d%
}%
\def\superexpandafterloop#1#2{%
\dfork{#2}{\stopromannumeral#1}{\superexpandafterloop{#1#1\expandafter}}%
}%
%%----------------------------------------------------------------------
\def\superexpandafter{\romannumeral\startsuperexpandafterloop}%
\newlinechar=`^^J
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
\superexpandafter{0}%
}%
\message{^^J^^Jn=0:^^J\string\test: \meaning\test}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
\superexpandafter{1}%
}%
\message{^^J^^Jn=1:^^J\string\test: \meaning\test}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
\superexpandafter{2}%
}%
\message{^^J^^Jn=2:^^J\string\test: \meaning\test}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
\superexpandafter{3}%
}%
\message{^^J^^Jn=3:^^J\string\test: \meaning\test}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
\superexpandafter{4}%
}%
\message{^^J^^Jn=4:^^J\string\test: \meaning\test}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
\superexpandafter{5}%
}%
\message{^^J^^Jn=5:^^J\string\test: \meaning\test}
\bye
Messages on the console/in the .log-file:
n=0:
\test: macro:->
n=1:
\test: macro:->\expandafter
n=2:
\test: macro:->\expandafter \expandafter \expandafter
n=3:
\test: macro:->\expandafter \expandafter \expandafter \expandafter \expandafter
\expandafter \expandafter
n=4:
\test: macro:->\expandafter \expandafter \expandafter \expandafter \expandafter
\expandafter \expandafter \expandafter \expandafter \expandafter \expandafter
\expandafter \expandafter \expandafter \expandafter
n=5:
\test: macro:->\expandafter \expandafter \expandafter \expandafter \expandafter
\expandafter \expandafter \expandafter \expandafter \expandafter \expandafter
\expandafter \expandafter \expandafter \expandafter \expandafter \expandafter
expandafter \expandafter \expandafter \expandafter \expandafter \expandafter \e
xpandafter \expandafter \expandafter \expandafter \expandafter \expandafter \ex
pandafter \expandafter
But a command whose expansion-cascade producess 2n - 1 tokens \expandafter with two "hits" by \expandafter, so that 2n - 1; n = 2 tokens \expandafter, i.e., three tokens \expandafter, are needed for producing these two "hits", might not be as useful as a command where "hitting" with one \expandafter produces n "hits" with \expandafter.
I.e., a command which is about producing "hits" by \expandafter instead of being about producing tokens \expandafter.
This is what the following command \Expandtimes is about—the code is almost the same as that of \superexpandafterloop.
The only difference is the place of \stopromannumeral:
%%----------------------------------------------------------------------
%% Hit \romannumeral of the expression
%% \romannumeral\Expandtimes{<number n>}<tokens>
%% by \expandafter once and <tokens> is hit by \expandafter n times.
%%
%% I.e., you need only one \expandafter-chain to hit \romannumeral
%% instead of needing n \expandafter-chains each to hit <tokens>.
%%
%% n is a non-negative integer.
%% <number n> is a TeX <number> which represents the value of
%% the non-negative integer n.
%%----------------------------------------------------------------------
\chardef\stopromannumeral=`\^^00 %
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\def\innerdfork#1d#2#3dd{#2}%
\def\dfork#1{\innerdfork#1{\firstoftwo}d{\secondoftwo}dd}%
\def\Expandtimes#1{%
\expandafter\Expandtimesloop\expandafter{\expandafter}%
\romannumeral\number\number#1 000d%
}%
\def\Expandtimesloop#1#2{%
\dfork{#2}{#1\stopromannumeral}{\Expandtimesloop{#1#1\expandafter}}%
}%
%%----------------------------------------------------------------------
\def\MacroNotExpanded{\OnceExpanded 1 }%
\def\OnceExpanded{\TwiceExpanded 2 }%
\def\TwiceExpanded{\ThreeTimesExpanded 3 }%
\def\ThreeTimesExpanded{\FourTimesExpanded 4 }%
\def\FourTimesExpanded{\FiveTimeExpanded 5 }
\newlinechar=`^^J
\expandafter\def
\expandafter\test
\expandafter{%
\romannumeral\Expandtimes{0}\MacroNotExpanded 0%
}%
\message{^^J^^Jn=0:^^J\string\test: \meaning\test}
\expandafter\def
\expandafter\test
\expandafter{%
\romannumeral\Expandtimes{1}\MacroNotExpanded 0%
}%
\message{^^J^^Jn=1:^^J\string\test: \meaning\test}
\expandafter\def
\expandafter\test
\expandafter{%
\romannumeral\Expandtimes{2}\MacroNotExpanded 0%
}%
\message{^^J^^Jn=2:^^J\string\test: \meaning\test}
\expandafter\def
\expandafter\test
\expandafter{%
\romannumeral\Expandtimes{3}\MacroNotExpanded 0%
}%
\message{^^J^^Jn=3:^^J\string\test: \meaning\test}
\expandafter\def
\expandafter\test
\expandafter{%
\romannumeral\Expandtimes{4}\MacroNotExpanded 0%
}%
\message{^^J^^Jn=4:^^J\string\test: \meaning\test}
\expandafter\def
\expandafter\test
\expandafter{%
\romannumeral\Expandtimes{5}\MacroNotExpanded 0%
}%
\message{^^J^^Jn=5:^^J\string\test: \meaning\test}
\bye
Messages on the console/in the .log-file:
n=0:
\test: macro:->\MacroNotExpanded 0
n=1:
\test: macro:->\OnceExpanded 1 0
n=2:
\test: macro:->\TwiceExpanded 2 1 0
n=3:
\test: macro:->\ThreeTimesExpanded 3 2 1 0
n=4:
\test: macro:->\FourTimesExpanded 4 3 2 1 0
n=5:
\test: macro:->\FiveTimeExpanded 5 4 3 2 1 0
Be aware that with the above varianf of \Expandtimesloop the amount of tokens accumulated in the first argument increases exponentially.
With the following variant of \Expandtimesloop that amount increases linearly at the cost of by and by getting (n+1) \romannumeral-expansions simultaneously:
\def\Expandtimesloop#1#2{%
\dfork{#2}{#1\stopromannumeral}{%
\Expandtimesloop{\expandafter\expandafter\expandafter\stopromannumeral\romannumeral#1}%
}%
}%
If you prefer simultaneous \csname-expansions instead, you can do:
\def\Expandtimesloop#1#2{%
\dfork{#2}{#1\stopromannumeral}{%
\Expandtimesloop{\csname expandafter\expandafter\expandafter\expandafter\endcsname#1}%
}%
}%
- 28,770
This is how it is done in the tokcycle package, courtesy of some macros provided by Christian Tellechea.
In this context, we expand the argument a number of times specified by the optional argument to \addcytoks and then add those tokens to the end of the \cytoks token list. The syntax is \addcytoks[n]{<argument>}, where n can be an integer denoting the number of expansions, where no expansion is provided if the optional argument is omitted, and the argument is fully expanded if the optional argument is given as x.
\documentclass{article}
\usepackage[T1]{fontenc}
\newtoks\cytoks
\makeatletter
\def\tc@ifempty#1{\tc@testxifx{\expandafter\relax\detokenize{#1}\relax}}
\long\def\tc@exfirst#1#2{#1}
\long\def\tc@exsecond#1#2{#2}
\long\def\tc@testxifx{\tc@earg\tctestifx}
\long\def\tc@earg#1{\expandafter#1\expandafter}
\long\def\tc@xarg#1#2{\tc@earg#1{\expanded{#2}}}
\long\def\tctestifcon#1{#1\expandafter\tc@exfirst\else\expandafter\tc@exsecond\fi}
\long\def\tctestifx#1{\tctestifcon{\ifx#1}}
\long\def\tctestifnum#1{\tctestifcon{\ifnum#1\relax}}
% SUPPORT MACROS FOR TOKENIZED OUTPUT: \addcytoks[<expansion level>]{<arg>}
% (CONTRIBUTED BY CHRISTIAN TELLECHEA)
\def\addcytoks{\futurelet\nxttok\addcytoks@A}
\long\def\tc@addtotoks#1{\cytoks\expandafter{\the\cytoks#1}}
\def\addcytoks@A{\tctestifx{[\nxttok}\addcytoks@B\tc@addtotoks}
\long\def\addcytoks@B[#1]#2{\tc@ifempty{#1}\tc@addtotoks
{\tctestifx{x#1}{\tc@xarg\tc@addtotoks}{\addcytoks@C{#1}}}{#2}}
\def\addcytoks@C#1{\tctestifnum{#1>0}{\tc@earg\addcytoks@C
{\the\numexpr#1-1\expandafter}\expandafter}\tc@addtotoks}
\makeatother
\begin{document}
\def\a{\b}
\def\b{\c}
\def\c{\d}
\def\d{\e}
\def\e{\f}
\def\f{\g}
\def\g{\h}
\def\h{i}
\addcytoks{\a}
\addcytoks[1]{\a}
\addcytoks[2]{\a}
\addcytoks[3]{\a}
\addcytoks[4]{\a}
\addcytoks[5]{\a}
\addcytoks[6]{\a}
\addcytoks[7]{\a}
\addcytoks[8]{\a}
\addcytoks[9]{\a}
\detokenize\expandafter{\the\cytoks}
\end{document}
- 237,551

\expandafteris a primitive, and is therefore executed, whereas and\superexpandafterwill be a macro and will itself need expansion. There are various approaches which need various numbers of expansions. Perhaps you might specify how many we're allowed? (I think something similar was discussed by the NTS while writing e-TeX, but as a potential primitive. Never happened, of course.) – Joseph Wright Jan 01 '11 at 19:39\eafor\expandafterin this comment.] The main problem is that in\ea\ea\ea \foo \ea\ea\ea \bar \baz, we can definitely not replace each\ea\ea\eaby a single macro separately. Maybe it would be possible to write something along the lines of\superexpandafter{permutation of [1,n]}that, when expanded (once? twice?), would yield the result of expanding the n following macros in a specific order. Then, we can hopefully use it in a sensible way, never preceeded by\expandafters. --- Of course, a primitive would be the nicest :). – Bruno Le Floch Jan 01 '11 at 21:33