For example if I have a macro \foo which takes 3 arguments and
wish to have passed the expansion of the macro \barA as first
argument to \foo, the expansion of the macro \barB as second
argument to \foo and the expansion of the macro \barC as third
argument to \foo, things might get cumbersome.
You cannot just write "\foo{\barA}{\barB}{\barC}" because then
you get the tokens as arguments, not their expansion.
An _expandable_ solution could be "reversed-expansion-order-accu-
mulation" via the second argument of \PassFirstToSecond:
\def\PassFirstToSecond#1#2{#2{#1}}
\expandafter\PassFirstToSecond\expandafter{\barC}{%
\expandafter\PassFirstToSecond\expandafter{\barB}{%
\expandafter\PassFirstToSecond\expandafter{\barA}{%
\foo%
}%
}%
}
[ That yields:
\PassFirstToSecond{<toplevel-expansion of \barC>}{%
\expandafter\PassFirstToSecond\expandafter{\barB}{%
\expandafter\PassFirstToSecond\expandafter{\barA}{%
\foo%
}%
}%
}
yields
\expandafter\PassFirstToSecond\expandafter{\barB}{%
\expandafter\PassFirstToSecond\expandafter{\barA}{%
\foo%
}%
}%
{<toplevel-expansion of \barC>}
yields
\PassFirstToSecond{<toplevel-expansion of \barB>}{%
\expandafter\PassFirstToSecond\expandafter{\barA}{%
\foo%
}%
}%
{<toplevel-expansion of \barC>}
yields
\expandafter\PassFirstToSecond\expandafter{\barA}{%
\foo%
}%
{<toplevel-expansion of \barB>}%
{<toplevel-expansion of \barC>}
yields
\PassFirstToSecond{<toplevel-expansion of \barA>}{%
\foo%
}%
{<toplevel-expansion of \barB>}%
{<toplevel-expansion of \barC>}
yields
\foo%
{<toplevel-expansion of \barA>}%
{<toplevel-expansion of \barB>}%
{<toplevel-expansion of \barC>}
]
But that solution still looks cumbersome to me.
In case you don't want toplevel-expansion of some of the argu-
ments/tokens but some deeper level of expansion, things will be
more complicated.
Therefore I'd prefer an expandable macro
\expandargs{<token-sequence>}{%
{{<expansion-depth 1>}{<argument-1>}{<brace-removal-depth 1>}}%
{{<expansion-depth 2>}{<argument-2>}{<brace-removal-depth 2>}}%
...
{{<expansion-depth N>}{<argument-N>}{<brace-removal-depth N>}}%
}%
which recursively iterates a list of argument-triplets held in
its second argument, passing the <expansion-depth K>-level-ex-
pansion of <argument-K> as K-th argument to <token-sequence>
after stripping at most <brace-removal-depth K> brace-levels
from that <expansion-depth K>-level-expansion of <argument-K>.
In case you leave the <expansion-depth K>-part empty, expansion
should take place until the first resulting token is not
expandable.
In case you leave the <brace-removal-depth-K>-part empty, brace-
removal should take place until there are no more brace-levels
surrounding the entire argument.
E.g.,
\def\one{\two}
\def\two{\three}
\def\three{\four}
\def\four{\five}
\def\five{\six}
\def\six{{{6}}}
\expandargs{<token-sequence>}{%
{{0}{\one}{0}}%
{{1}{\one}{0}}%
{{3}{\one}{0}}%
{{5}{\one}{0}}%
{{6}{\one}{2}}%
{{2}{\one}{2}}%
{{}{\one}{}}%
{{}{\one}{0}}%
{{}{\one}{1}}%
}%
should yield:
<token-sequence>{\one}%
{\two}%
{\four}%
{\six}%
{6}%
{\three}%
{6}%
{{{6}}}%
{{6}}
By now I spent almost an hour on the problem and I *think* I
accomplished such a macro with a lot of \romannumeral-expansion-
trickery. It requires a bunch of code. It is very slow.
I'd be grateful for any pointers on how
- to improve the implementation,
- to reduce the amount of code,
- to get things working faster.
Is it true that things get slow when nesting \romannumeral-
expansion?
Sincerely
Ulrich
[There is a bunch of code following. In case you are interested,
please make sure to view the message in "original format".]
%%
%% Copyright (C) 2010 by Ulrich Diez (eu_an...@web.de)
%%
%% This work may be distributed and/or modified under the
%% conditions of the LaTeX Project Public Licence (LPPL), either
%% version 1.3 of this license or (at your option) any later
%% version. (The latest version of this license is in:
%% http://www.latex-project.org/lppl.txt
%% and version 1.3 or later is part of all distributions of LaTeX
%% version 1999/12/01 or later.)
%% The author of this work is Ulrich Diez.
%% This work has the LPPL maintenance status 'not maintained'.
%% Usage of any/every component of this work is at your own risk.
%% There is no warranty - neither for probably included
%% documentation nor for any other part/component of this work.
%% If something breaks, you usually may keep the pieces.
%%
\errorcontextlines=10000
\documentclass[twoside, a4paper]{article}
\makeatletter
%%===============================================================
%%---------------------------------------------------------------
%% Stopper for \romannumeral-Expansion:
%%...............................................................
\newcommand\@rmstop{0 }
%%---------------------------------------------------------------
%% Pass first argument as parameter to second argument:
%%...............................................................
\newcommand\PassFirstToSecond[2]{#2{#1}}
%%---------------------------------------------------------------
%% Check if argument is blank / is null:
%%...............................................................
\newcommand\@ifnull[3]{%
\romannumeral\iffalse{\fi\expandafter\@secondoftwo\expandafter
{\expandafter{\string#1}\expandafter\@secondoftwo\string}%
\expandafter\@firstoftwo\expandafter{\iffalse}\fi0 #3}{0 #2}%
}
\newcommand\@ifblank[1]{%
\romannumeral\expandafter\expandafter\expandafter\@gobble
\expandafter\@ifnull\expandafter{\@gobble#1.}%
}
%%---------------------------------------------------------------
%% Check if brace-balanced argument starts with a bgroup-char:
%%...............................................................
\newcommand\@ifbrace[3]{%
\romannumeral\expandafter\@secondoftwo\expandafter{\expandafter
{\string#1.}\expandafter\@firstoftwo\expandafter{\iffalse}\fi
0 #2}{0 #3}%
}
%%---------------------------------------------------------------
%% Check if brace-balanced argument starts with a space-char:
%%...............................................................
\newcommand\@ifLeadSpace[3]{%
\romannumeral\csname
@\@ifnull{#1}{second}{\iffalse{\fi\@@ifLeadSpace.#1 }%
{}}oftwo\endcsname{0 #2}{0 #3}%
}
\@ifdefinable\@@ifLeadSpace{\long\def\@@ifLeadSpace#1 {%
\expandafter\@ifnull\expandafter{\@secondoftwo#1{}}{first}{second}%
\expandafter\@secondoftwo\expandafter{\iffalse}\fi
}}
%%---------------------------------------------------------------
%% \rembraces{N}{<Argument>} -> In case <Argument> consists of
%% stuff that is surrounded by at
%% least N pairs of braces, N
%% pairs of braces will be removed.
%% Otherwise all surrounding braces
%% will be removed.
%% \rembraces{}{<Argument>} -> While <Argument> consists of stuff
%% that is surrounded by braces, bra-
%% ces will be removed from <Argument>
%%...............................................................
\newcommand\rembraces[1]{%
\romannumeral\@ifnull{#1}{\@@rembraces}{%
\expandafter\@rembraces\expandafter{\romannumeral#1000}%
}%
}
\newcommand\@rembraces[2]{%
\@ifnull{#1}{\@rmstop#2}{%
\expandafter\@ifnull\expandafter{\@gobble#2}{%
\@ifbrace{#2}{%
\expandafter\PassFirstToSecond
\expandafter{\@firstofone#2}%
{\expandafter\@rembraces\expandafter{\@gobble#1}}%
}{%
\@rmstop#2%
}%
}{%
\@rmstop#2%
}%
}%
}
\newcommand\@@rembraces[1]{%
\expandafter\@ifnull\expandafter{\@gobble#1}{%
\@ifbrace{#1}{%
\expandafter\@@rembraces\expandafter{\@firstofone#1}%
}{\@rmstop#1}%
}{%
\@rmstop#1%
}%
}
%%---------------------------------------------------------------
%% \expandtimes{N}{<Argument>} -> N expandafter-chains will hit
%% on the place of <Argument> and
%% the result will be spit out
%% without the braces surrounding
%% <Argument>.
%% \expandtimes{}{<Argument>} -> expandafter-chains will hit on the
%% place of <Argument> until something
%% occurs on this place whose first
%% token is not expandable and the
%% result will be spit out without
%% the braces surrounding <Argument>.
%%...............................................................
\newcommand\expandtimes[2]{%
\romannumeral\@expandtimes{2}%
\expandafter\@firstofone\expandafter{%
\romannumeral
\@ifnull{#1}{%
\expandafter\expandafter\expandafter\@rmstop
\expandafter\@firstofone
\expandafter{\romannumeral\@@expandtimes{#2}}%
}{%
\expandafter\expandafter\expandafter\@rmstop
\expandafter\@firstofone
\expandafter{\romannumeral
\@expandtimes{#1}#2}%
}%
}%
}
%
% \romannumeral\@expandtimes{<number>}<Trailing Stuff>
% -> <Trailing Stuff> will be hit by <number> \expandafter-chains.
% !!!!!Be aware that \@expandtimes just hits the <Trailing Stuff>
% which means it has no effect in case <Trailing Stuff>
% is something that is nested into braces - braces are not
% expandable.
%
\newcommand\@expandtimes[1]{%
\expandafter\@@@expandtimes
\expandafter{\romannumeral#1000}{}%
}
\newcommand\@@@expandtimes[2]{%
\@ifnull{#1}{\@rmstop}{%
\expandafter\@ifnull\expandafter{\@gobble#1}{%
\expandafter\@rmstop#2\romannumeral\expandafter\@rmstop
}{%
\expandafter\@@@expandtimes\expandafter{%
\@gobble#1%
}{%
#2\romannumeral\expandafter\expandafter
\expandafter\@rmstop
}%
}%
}%
}
%
% \romannumeral\@@expandtimes{<Stuff>}
% -> \expandafter-chains will hit <Stuff>
% until the first thing of <Stuff> a non-exandable token.
% !!!!!Be aware that \@@expandtimes hits the <Stuff> which is
% _inside_ the braces!!!!!
%
\newcommand\@@expandtimes[1]{%
\@ifnull{#1}{\@rmstop#1}{%
\@ifbrace{#1}{%
\@rmstop#1%
}{%
\@ifLeadSpace{#1}{%
\@rmstop#1%
}{%
\expandafter\expandafter
\expandafter\@ifexpandable
\@TrimAllButFirstLoopUndelimited{#1\SeLDom}{%
\expandafter\@@expandtimes\expandafter{#1}%
}{\@rmstop#1}%
}%
}%
}%
}
\newcommand\@ifexpandable[3]{%
\romannumeral\@ifbrace{#1}\@secondoftwo{%
\@ifblank{#1}%
\@secondoftwo
{%
\expandafter\ifx\noexpand#1#1%
\expandafter\@secondoftwo
\else
\expandafter\@firstoftwo
\fi
}%
}{0 #2}{0 #3}%
}
%%---------------------------------------------------------------
%% \expandandargs{<token-sequence>}{%
%% {{<expansion-depth 1>}{<argument-1>}{<brace-removal-depth 1>}}%
%% {{<expansion-depth 2>}{<argument-2>}{<brace-removal-depth 2>}}%
%% ...
%5 {{<expansion-depth N>}{<argument-N>}{<brace-removal-depth N>}}%
%% }%
%%
%% recursively iterates a list of argument-triplets held in its
%% second argument, passing the <expansion-depth K>-level-expan-
%% sion of <argument-K> as K-th argument to <token-sequence>
%% after stripping at most <brace-removal-depth K> brace-levels
%% from that <expansion-depth K>-level-expansion of <argument-K>.
%%
%% In case you leave the <expansion-depth K>-part empty, expan-
%% sion takes place until the first resulting token is not ex-
%% pandable.
%% In case you leave the <brace-removal-depth-K>-part empty,
%% brace-removal should take place until there are no more
%% brace-levels surrounding the entire argument.
%%...............................................................
\newcommand\expandargs[2]{%
\romannumeral\@expandtimes{2}%
\expandafter\@firstofone
\expandafter{%
\romannumeral\@expandargs{#2}{#1}}%
}
\newcommand\@expandargs[2]{%
\@ifnull{#1}{\@rmstop#2}{%
\expandafter\PassFirstToSecond
\romannumeral\@expandtimes{3}\expandafter\@@expandargs
\romannumeral\@expandtimes{3}%
\expandafter\expandafter\expandafter\@firstofone
\@TrimAllButFirstLoopUndelimited{#1\SeLDom}{#2}%
{\expandafter\@expandargs\expandafter{\@gobble#1}}%
}%
}
\newcommand\@@expandargs[4]{%
\romannumeral\expandafter\@rmstop
\expandafter
{\romannumeral\@expandtimes{2}%
\expandafter\PassFirstToSecond
\expandafter{%
\romannumeral\@expandtimes{4}\expandafter\PassFirstToSecond
\expandafter{%
\romannumeral\@expandtimes{2}%
\expandtimes{#1}{#2}}{\rembraces{#3}}}{#4}}%
}
%%---------------------------------------------------------------
%% Get the first element of an undelimited-arguments-list:
%% Pass the <argument-list> with the token \SeLDom attached
%% as argument to \@TrimAllButFirstLoopUndelimited,
%% \@TrimAllButFirstLoopUndelimited{<argument-list>\SeLDom}
%% , and you will obtain
%% - either, in case the list s not empty,
%% the first argument of that list, with a surrounding pair
%% of catcode-1/2-characters replaced by a surrounding
%% pair of catcode 1/2-braces
%% - or, in case the list is empty,
%% an "empty argument" formed by a pair of catcode 1/2-braces.
%% The token \SeLDom may occur in the <argument-list> as well.
%% In that case gobbling anything but the first argument will
%% just take more iterations.
%%...............................................................
\newcommand*\@gobblesp[1]{\def\@gobblesp#1{}}\@gobblesp{ }
\@ifdefinable\@getfirsteltUndelimited{%
\long\def\@getfirsteltUndelimited#1#2\SeLDom{{#1}}%
}
\newcommand\@TrimAllButFirstLoopUndelimited[1]{%
\romannumeral
\expandafter\expandafter\expandafter\@ifnull
\expandafter\expandafter\expandafter{%
\expandafter\@gobble\@getfirsteltUndelimited#1}{%
\expandafter\expandafter\expandafter\@rmstop
\expandafter\@firstofone\expandafter{%
\@getfirsteltUndelimited#1}%
}{%
\expandafter\expandafter\expandafter\@gobble
\expandafter\@TrimAllButFirstLoopUndelimited
\expandafter{\@getfirsteltUndelimited#1}%
}%
}
%%===============================================================
\begin{document}
\parindent\z@
\parskip\baselineskip
\ttfamily\selectfont
\def\one{\two1 }
\def\two{\three2 }
\def\three{\four3 }
\def\four{\five4 }
\def\five{\six5 }
\def\six{6 }
|\expandafter\string
\romannumeral\expandafter\expandafter
\expandafter \@rmstop
\expandtimes{0}{\one}|
|\expandafter\string
\romannumeral\expandafter\expandafter
\expandafter \@rmstop
\expandtimes{1}{\one}|
|\expandafter\string
\romannumeral\expandafter\expandafter
\expandafter \@rmstop
\expandtimes{2}{\one}|
|\expandafter\string
\romannumeral\expandafter\expandafter
\expandafter \@rmstop
\expandtimes{3}{\one}|
|\expandafter\string
\romannumeral\expandafter\expandafter
\expandafter \@rmstop
\expandtimes{4}{\one}|
|\expandafter\string
\romannumeral\expandafter\expandafter
\expandafter \@rmstop
\expandtimes{5}{\one}|
|\expandafter\string
\romannumeral\expandafter\expandafter
\expandafter \@rmstop
\expandtimes{6}{\one}|
|\expandafter\string
\romannumeral\expandafter\expandafter
\expandafter \@rmstop
\expandtimes{}{\one}|
\expandafter\def\expandafter\test\expandafter{%
\romannumeral\@expandtimes{2}%
\expandargs{BLA}{%
{{0}{\one}{0}}%
{{1}{\one}{0}}%
{{2}{\one}{0}}%
{{3}{\one}{0}}%
{{4}{\one}{0}}%
{{5}{\one}{0}}%
{{6}{\one}{0}}%
{{}{\one}{0}}%
}%
}%
|\meaning\test|
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
\expandtimes{}{\one}}%
\meaning\test
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
\expandtimes{6}{\one}}%
\meaning\test
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
\expandtimes{7}{\one}}%
\meaning\test
\expandafter\def
\expandafter\test
\expandafter{%
\romannumeral\@expandtimes{0}\one}%
\meaning\test
\expandafter\def
\expandafter\test
\expandafter{%
\romannumeral\@expandtimes{1}\one}%
\meaning\test
\expandafter\def
\expandafter\test
\expandafter{%
\romannumeral\@expandtimes{}\one}%
\meaning\test
\expandafter\def
\expandafter\test
\expandafter{%
\romannumeral\@@expandtimes{ \one}}%
\meaning\test
\expandafter\def
\expandafter\test
\expandafter{%
\romannumeral\@@expandtimes{\one}}%
\meaning\test
\def\one{\two}
\def\two{\three}
\def\three{\four}
\def\four{\five}
\def\five{\six}
\def\six{{{6}}}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
\expandargs{<token-sequence>}{%
{{0}{\one}{0}}%
{{1}{\one}{0}}%
{{3}{\one}{0}}%
{{5}{\one}{0}}%
{{6}{\one}{2}}%
{{2}{\one}{2}}%
{{}{\one}{}}%
{{}{\one}{0}}%
{{}{\one}{1}}%
}%
}%
|\meaning\test|
\end{document}
While it doesn't answer your question, I think it's worth commenting
that this is very reminiscent of the l3expan LaTeX3 module, and the idea
of 'o' expansion. There used to be 'd' (double expansion) as well, which
can be done similarly. We dropped that as it was pretty rare: how often
do you need these 'expand n times' cases?
--
Joseph Wright
> how often
> do you need these 'expand n times' cases?
Whenever I want to do something in an expandable way
which seems more complicated.
E.g., I needed an expandable routine which takes a list of
tokens and delivers the same result as if performing \edef
on the token-list and putting the stringification of each token
of the expansin of the resulting definition-text into catcode
1/2-braces. You can't simply use \detokenize because
then you don't have the stringification of one token
separated from the stringification of another token.
Besides this -afaik- \edef would require #-doubling and
you have a lot of recursion-fun whenever material within the
token-list is nested into braces.
I think I got a routine for that, but it is awfully slow.
I think this is due to the way in which \expandargs is currently
implemented.
E.g. in case I wish the macro \foo to get arguments
\foo{<2nd-level-expansion of \ArgA>}%
{{<1th-level-expansion of \InnerArgBa>}{<4th-level-expansion of \InnerArgBb>}}%
{<5th-level-expansion of \ArgC>}%
{<3rd-level-expansion of \ArgD>}
, I can simply write
\expandargs{\foo}{%
{{2}{\ArgA}{0}}%
{{2}{% \expandargs takes 2 expansion-steps
\expandargs{}{%
{{1}{\InnerArgBa}{0}}%
{{4}{\InnerArgBb}{0}}%
}%
}{0}}%
{{5}{\ArgC}{0}}%
{{3}{\ArgD}{0}}%
}%
Sincerely
Ulrich
> Sometimes I'm in the situation that I need tokens expanded so that their
> expansion can be used as macro-argument.
>
> For example if I have a macro \foo which takes 3 arguments and wish to
> have passed the expansion of the macro \barA as first argument to \foo,
> the expansion of the macro \barB as second argument to \foo and the
> expansion of the macro \barC as third argument to \foo, things might get
> cumbersome.
>
> You cannot just write "\foo{\barA}{\barB}{\barC}" because then you get
> the tokens as arguments, not their expansion.
This might be a stupid question, but I might just learn something from
this. Wouldn't a simple \the\barA etc. do the trick?