How does your mind bend \expandafter to its will?
\in@{\foo}{\cslist} % <--- What combination of \expandafter is needed here?
If \foo
is first expanded, then we have the problem, that \expandafter
cannot jump over serveral tokens at once, also the number of tokens is not known. Therefore the latest token is expanded first. But at this stage we cannot add the \expandafter
, because we have to insert the \expandafter
tokens for \foo
first:
\ex\in@\ex{\foo}{\cslist}
Then we add the outmost \expandafter
chain to expand \cslist
. The next line uses \EX
for the new \expandafter
to make the difference between the stages visible:
\EX\ex\EX\in@\EX\ex\EX{\EX\ex\EX\foo\EX}\EX{\cslist}
Result:
\ex\ex\ex\in@\ex\ex\ex{\ex\ex\ex\foo\ex}\ex{\cslist}
A more generic algorithm would be:
Establish an order of expansions:
Collapsing expansions: We have to make sure, that the number of tokens is known and we can insert
\expandafter
between them, if we need to jump over them. Therefore constructs like\csname
need to be expanded at an earlier level. This allows also the use of arguments inside\csname
with an unknown number of tokens, because the\csname
construct becomes one single command token after one expansion step.Note: See also the trick below, that
\csname
can be used to expand stuff afterwards.Expanding expansions, e.g.
\cslist
above, on the right side have to come first, because we cannot jump over a unknown number of tokens.
Now we can add
\expandafter
chains from the start to the token that needs expanding. The order from the previous step is now reversed. First the chain for the token that is last expanded is inserted, e.g.:0. \a\b\last\c\first 1. \EX\a\EX\b\last\c\first % \EX inserted
Then we go backwards in time to expand the token that needs expansion before the last:
=1. \ex\a\ex\b\last\c\first 2. \EX\ex\EX\a\EX\ex\EX\b\EX\last\EX\c\first % \EX inserted =2. \ex\ex\ex\a\ex\ex\ex\b\ex\last\ex\c\first
"Tricks"
Sometimes TeX helps to save some \expandafter
.
Expanding after
\csname
:Let's assume
\foo
and\cslist
are not given explicitly but constructed via\csname
:\in@{\csname foo\endcsname}{\csname cslist\endcsname}
A naive approach would require four expansions waves:
- expanding the first
\csname
to get one token\foo
- expanding the second
\csname
to get\cslist
- expanding
\cslist
- expanding
\foo
Result: Start with 24-1
\expandafter
(= 15).This can be reduced: TeX expands the tokens between
\csname
and\endcsname
until nothing expandable is left to form a command sequence. The following uses this to get\cslist
and its expansion before\foo
is constructed:\csname foo\ex\ex\ex\endcsname \ex\ex\ex}\ex\ex\ex{\csname cslist\endcsname}
And the whole expression with the expansion of
\foo
:\ex\ex\ex\in@\ex\ex\ex{\csname foo\ex\ex\ex\endcsname \ex\ex\ex}\ex\ex\ex{\csname cslist\endcsname}
The result are 15
\expandafter
in total.- expanding the first
Expanding arguments of some TeX primitives such as
\uppercase
.Let's assume
\foo
expands to a word that should be converted to uppercase:\ex\uppercase\ex{\foo}
Here we can save the first
\expandafter
, because\uppercase
already expands the next tokens until it gets the opening brace:\uppercase\ex{\foo}
Other primitives:
\detokenize
,\scantokens
,\message
.Caveat: If someone redefines
\uppercase
as macro, this trick will fail obviously.
You can exploit \unexpanded
:
\documentclass{article}
\makeatletter
% both arguments are expanded once
\newcommand{\xxin@}[2]{%
\begingroup\edef\x{\endgroup
\noexpand\in@{\unexpanded\expandafter{#1}}{\unexpanded\expandafter{#2}}%
}\x
}
% the first argument is expanded once
\newcommand{\xnin@}[2]{%
\begingroup\edef\x{\endgroup
\noexpand\in@{\unexpanded\expandafter{#1}}{\unexpanded{#2}}%
}\x
}
% the second argument is expanded once
\newcommand{\nxin@}[2]{%
\begingroup\edef\x{\endgroup
\noexpand\in@{\unexpanded{#1}}{\unexpanded\expandafter{#2}}%
}\x
}
\makeatletter
\begin{document}
\makeatletter
\def\foo{foo}
\def\cslist{foo,bar,baz}
\in@{foo}{foo,bar,baz}
\ifin@ true\else false\fi
\xnin@{\foo}{foo,bar,baz}
\ifin@ true\else false\fi
\nxin@{foo}{\cslist}
\ifin@ true\else false\fi
\xxin@{\foo}{\cslist}
\ifin@ true\else false\fi
\makeatother
\end{document}
This prints “true” for all cases.
The mandatory LaTeX3 solution, where \tl_if_in:nnTF
(with variants) is provided.
\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\cs_generate_variant:Nn \tl_if_in:nnTF { oo }
\ExplSyntaxOff
\begin{document}
\def\foo{foo}
\def\cslist{foo,bar,baz}
\ExplSyntaxOn
\tl_if_in:nnTF{foo,bar,baz}{foo}{true}{false}\par
\tl_if_in:noTF{foo,bar,baz}{\foo}{true}{false}\par
\tl_if_in:onTF{\cslist}{foo}{true}{false}\par
\tl_if_in:ooTF{\cslist}{\foo}{true}{false}\par
\ExplSyntaxOff
\end{document}
\in@{\foo}{\cslist}
If you don't need pure expansion and can afford an assignment, that can simplify things
\def\tmp{\expandafter\in@\expandafter{\foo}}
\expandafter\tmp\expandafter{\cslist}
only needs four \expandafter