Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

TeX Pearls: \reverse

13 views
Skip to first unread message

Pluto

unread,
Nov 3, 2009, 11:27:41 AM11/3/09
to
The following is from TeX Pearls (http://www.gust.org.pl/projects/
pearls)

1. Why is nothing printed after the code is run?

2. Why does the code still run if \def\stopreverse{\noexpand
\stopreverse} is commented out?

\documentclass{minimal}
%\tracingmacros=1
\def\afterfi#1#2\fi{\fi#1}
\def\reverse#1{\reverseX{}#1\stopreverse}
\def\stopreverse{\noexpand\stopreverse}
\def\reverseX#1#2{%
\ifx\stopreverse#2
\afterfi{#1}%
\else
\afterfi{\reverseX{#2#1}}%
\fi
}
\begin{document}

%\def\foo{foo}
%\def\bar{bar}

\toks0=\expandafter{\if0\reverse{abc\foo def\bar ghi0}\fi}
\the\toks0

\toks0=\expandafter{\if0\reverse{abc\foo def\bar ghi}\fi}
\the\toks0

\end{document}

Donald Arseneau

unread,
Nov 3, 2009, 5:23:17 PM11/3/09
to
On Nov 3, 8:27 am, Pluto <a.m...@rocketmail.com> wrote:
> The following is from TeX Pearls (http://www.gust.org.pl/projects/
> pearls)
>
> 1. Why is nothing printed after the code is run?

Because char "0" is not equal to char " ". Change your definition to
\ifx\stopreverse#2%

> 2. Why does the code still run if \def\stopreverse{\noexpand
> \stopreverse} is commented out?

It "runs" but reversal is terminated by any undefined command, like
\foo or \bar in your sample, so the \if test is false.

> \toks0=\expandafter{\if0\reverse{abc\foo def\bar ghi0}\fi}

3. This leaves the trailing \fi in \toks0, and can lead to unmatched
\if...\fi errors.

Donald Arseneau

Ulrich D i e z

unread,
Nov 3, 2009, 6:29:44 PM11/3/09
to
Pluto wrote:

I added some commenting.

Ulrich


\documentclass{minimal}
%\tracingmacros=1
\def\afterfi#1#2\fi{\fi#1}
\def\reverse#1{\reverseX{}#1\stopreverse}

\def\stopreverse{\noexpand\stopreverse}

%%% \stopreverse is defined to \noexpand\stopreverse to
%%% make sure that it is defined. If it was undefined,
%%% any other undefined token would yield a true \ifx-
%%% condition and thus erroneously terminate the recursion.

\def\reverseX#1#2{%
\ifx\stopreverse#2%
%%% In the line above you erroneously omitted a comment-char
%%% leading to erroneous insertion of a space-token.
%%5 before the reversed stuff in the last iteration.
%%% That space-token was compared to 0 of the
%%% \if0-construct leading to the condition being "false"...
%%% With this error you also found no \fi in the token-register
%%% because TeX stumbled over it when searching the \else-branch.


\afterfi{#1}%
\else
\afterfi{\reverseX{#2#1}}%
\fi
}
\begin{document}

\def\foo{foo}
\def\bar{bar}
%%% be aware that \bar is already defined in LaTeX and requires mathmode.

\toks0=\expandafter{\if0\reverse{abc\foo def\bar ghi0}\fi}

\showthe\toks0 % -> see the \fi!
\the\toks0

\toks0=\expandafter{\if0\reverse{abc\foo def\bar ghi}\fi}

\showthe\toks0 % -> no \fi because TeX stumbles over it while searching
% the \else-branch.
\the\toks0

%%% I'd use \romannumeral - You don't have this \fi-stuff in the
%%% token-register then. Also unbalanced \else or \fi won't
%%% erroneously terminate the \if-construct.

\toks0=\expandafter{\romannumeral\reverse{abc\foo def\bar ghi 0}}
\showthe\toks0
\the\toks0

\end{document}


You can code that \romannumeral-thingie into the macro:

\documentclass{minimal}
%\tracingmacros=1
\def\afterfi#1#2\fi{\fi#1}

\def\reverse#1{\romannumeral\reverseX{}#1\stopreverse}


\def\stopreverse{\noexpand\stopreverse}
\def\reverseX#1#2{%

\ifx\stopreverse#2%
\afterfi{0 #1}%


\else
\afterfi{\reverseX{#2#1}}%
\fi
}
\begin{document}

\def\foo{foo}
\def\bar{bar}
\toks0=\expandafter\expandafter\expandafter{\reverse{abc\foo def\bar ghi}}
\showthe\toks0
\the\toks0
\end{document}

Pluto

unread,
Nov 5, 2009, 1:57:51 PM11/5/09
to
> \end{document}- Hide quoted text -
>
> - Show quoted text -

Thanks. Everything is clear except the following:

>%%% With this error you also found no \fi in the token-register
>%%% because TeX stumbled over it when searching the \else-branch.
> \afterfi{#1}%
> \else
> \afterfi{\reverseX{#2#1}}%
> \fi
> }

Is the test

\if0\reverse{abc\foo def\bar ghi0}\fi

not complete, irrespective of whether it is true or false. Please why
can't the \if test see the trailing \fi?

Sincerely

Message has been deleted

Ulrich D i e z

unread,
Nov 5, 2009, 6:35:14 PM11/5/09
to
Pluto wrote:

> Is the test
>
> \if0\reverse{abc\foo def\bar ghi0}\fi
>
> not complete, irrespective of whether it is true or false. Please why
> can't the \if test see the trailing \fi?

Expanding stuff and evaluating \if..\else..\fi-constructs usually
does not happen in a single expansion-/evaluation-pass.

As a rule of thumb you can say that the effect of single-expansion
an \if-test is
- expanding stuff until finding items to compare
- "jumping" to the place where - depending on the result of evaluating
whether the condition is true or false - further evaluation/expansion
must continue.

\if aatrue\else false\fi

The condition is true. Evaluation/Expansion continues right behind
the second item to compare - that is behind the second "a" and
before the "t".

So as a result of expanding the \if-construct you get:
true\else false\fi

When during further processing/further expansion the associated
\else is encountered, then stuff will be discarded unexpanded/un-
evaluated until encountering the associated \fi.
But that happens in _later_ evaluation-steps.

If there is no \else-branch:

\if aatrue\fi

The condition is true. Expansion must continue right behind the
second item to compare - that is behind the second a and before
the "t".

So as a result of expanding the \if-construct you get
true\fi

The associated \fi will be encountered and discarded
in later evaluation/expansion-steps.

With the assignment

\toks0=\expandafter{\if0\reverse{abc\foo def\bar ghi0}\fi}

it is the same:
The \expandafter initiates expansion of \if which in turn
- yields total expansion of the \reverse-recursion until \if
can find two non-expandable tokens to compare.
Inside the braces of the token-register-assignment you
get:
\if 00ihg\bar fed\foo cba\fi
- while evaluating condition is true and delivers
ihg\bar fed\foo cba\fi
and that's what goes into the token-register.


\if abtrue\else false\fi

The condition is false. Expansion must continue behind the next
\else or behind the next \fi (with respect to \if-\else-\fi-nesting).

As there is an \else-branch, you get:

false\fi


\toks0=\expandafter{\if0\reverse{abc\foo def\bar ghi\else1}\fi}

The \expandafter initiates expansion of \if which in turn
- yields total expansion of the \reverse-recursion until \if
can find two non-expandable tokens to compare.
Inside the braces of the token-register-assignment you
get:
\if 01\else ihg\bar fed\foo cba\fi
- while evaluating condition is false and delivers
ihg\bar fed\foo cba\fi
and that's what goes into the token-register.


Ulrich

Pluto

unread,
Nov 6, 2009, 8:56:34 AM11/6/09
to

The \else inside the token list here causes problems. In fact, the
loop terminates prematurely (macro tracing gives the following as the
last expansions before \enddocument) and \toks0 is empty.

\reverseX #1#2->\ifx \stopreverse #2\afterfi {#1}\else \afterfi
{\reverseX {#2#
1}}\fi
#1<-\else ihg\baz fed\foo cba
#2<-1

Ulrich D i e z

unread,
Nov 6, 2009, 12:34:15 PM11/6/09
to
Pluto wrote:

> The \else inside the token list here causes problems. In fact, the
> loop terminates prematurely (macro tracing gives the following as the
> last expansions before \enddocument) and \toks0 is empty.

Ouch! You are right. What I said was wrong.

\documentclass{minimal}
%\tracingmacros=1
\def\afterfi#1#2\fi{\fi#1}
\def\reverse#1{\reverseX{}#1\stopreverse}
\def\stopreverse{\noexpand\stopreverse}
\def\reverseX#1#2{%

\ifx\stopreverse#2%


\afterfi{#1}%
\else
\afterfi{\reverseX{#2#1}}%
\fi
}
\begin{document}

\def\foo{foo}
\def\bar{bar}

\toks0=\expandafter{\if 01\else ihg\bar fed\foo cba\fi}
\the\toks0
\showthe\toks0

\toks0=\expandafter{\if0\reverse{abc\foo def\bar ghi\else1}\fi}

\the\toks0
\showthe\toks0

\toks0=\expandafter{\if0\reverse{abc\foo def\bar ghi0}\fi}

\the\toks0
\showthe\toks0

\toks0=\expandafter{\if0\reverse{a b c d e0}\fi}
\the\toks0
\showthe\toks0

\end{document}


\toks0=\expandafter{\if 01\else ihg\bar fed\foo cba\fi}
\showthe\toks0
yields: ihg\bar fed\foo cba\fi

But

\toks0=\expandafter{\if0\reverse{abc\foo def\bar ghi\else1}\fi}

does not yield that. I was wrong.

That's because of the token \else becoming part of the Argument #1
of the macro \reverseX while \if-\fi-matching is _independent_
from group-matching:


\if0\reverseX{ihg\bar fed\foo aab}{\else}1\stopreverse\fi

->

\toks0=\expandafter{\if0\ifx\stopreverse\else
\afterfi{ihg\bar fed\foo cab}%
\else
\afterfi{\reverseX{\else ihg\bar fed\foo cba}}%
\fi
1\stopreverse\fi}

->

\toks0=\expandafter{\if0%
\afterfi{\reverseX{\else ihg\bar fed\foo cba}}%
\fi
1\stopreverse\fi}

->

\toks0=\expandafter{\if0%
\fi\reverseX{\else ihg\bar fed\foo cba}%
1\stopreverse\fi}

-> \fi will match up \ifx

\toks0=\expandafter{\if0%
\reverseX{\else ihg\bar fed\foo cba}%
1\stopreverse\fi}

->

\toks0=\expandafter{\if0%
\ifx\stopreverse1%
\afterfi{\else ihg\bar fed\foo cba}\else \afterfi{\reverseX{1\else ihg\bar fed\foo cba}}\fi
\stopreverse\fi}

-> [ the \ifx-statement is false, so jump to the \ifx-matching \else ]

\toks0=\expandafter{\if0%
ihg\bar fed\foo cba}\else \afterfi{\reverseX{1\else ihg\bar fed\foo cba}}\fi % <- \ifx-statement-\fi]
\stopreverse\fi} % <- \if-statement-\fi]

-> [I *think* the \if-statement is false, so "jump" to the next
\else or \fi behind the \fi which matches the still unmatched
\ifx-statement.
The fun is that the extra-\else before the \ifx-statement's \fi does
not raise an error due to discarding due to the falseness of the
\if-statement. ]

\toks0={}


Sorry for not seeing this earlier and writing a bunch of crap.

By the way 1: The TeX-pearl \reverse removes space-tokens.
Until now I overlooked that circumstance. This is because
\reverseX takes undelimited arguments and fetching undelimited
arguments means disregarding spaces. Also it strips off braces.
Try \reverse{a b {c} d e}.

By the way 2: You can circumvent erroneous \if-\fi-matching by
macro-argument-content. You just need to ensure that arguments are
always placed _outside_ such \if-\else-\fi-constructs.
I like using \@firstoftwo and \@secondoftwo a lot.

The solution below does neither resolve the space-removal-issue
nor the brace-stripping-issue and it still depends on a
"sentinel-token" (\stopreverse) for ending the recursion.

Ulrich

\documentclass{minimal}
%\tracingmacros=1
\newcommand\reverse[1]{\romannumeral\reverseX{}#1{ }0\stopreverse}
\newcommand*\stopreverse[1]{\noexpand\stopreverse}
\makeatletter
\newcommand\reverseX[2]{%
\ifx\stopreverse#2%
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{#1}%
{\reverseX{#2#1}}%
}
\makeatother
\begin{document}

\def\foo{foo}
\def\bar{bar}

\toks0=\expandafter\expandafter\expandafter{\reverse{abc\foo def\bar ghi}}
\showthe\toks0

\toks0=\expandafter\expandafter\expandafter{\reverse{abc\foo def\bar ghi\else1}}
\showthe\toks0

\toks0=\expandafter\expandafter\expandafter{\reverse{abc\foo def\bar\fi\ifx ghi\else1}}
\showthe\toks0

\end{document}

Pluto

unread,
Nov 10, 2009, 12:01:50 PM11/10/09
to
> By the way 1: The TeX-pearl \reverseremoves space-tokens.

Please how can the \else be expanded without \fi after \@firstoftwo? I
tried out the code and hit a brick wall :)

Ulrich D i e z

unread,
Nov 10, 2009, 3:22:38 PM11/10/09
to
Pluto wrote:

> Please how can the \else be expanded without \fi after \@firstoftwo? I
> tried out the code and hit a brick wall :)

\ifx\stopreverse#2%


\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi

If the condition is true, you get:

\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi

The \expandafter goes on the \else and as the condition
is "true" and the \ifx unmatched, the expansion of the \else
yields removal of everything from the \else to the matching
\fi which will also be removed.
Only the token \@firstoftwo remains.

If the condition is false, everything will be discarded until
after the \else and you get:

\expandafter\@secondoftwo
\fi

The \expandafter goes on the \fi which will be removed and
match up the \ifx-construct.

Only the token \@secondoftwo remains.

Sincerely

Ulrich

Pluto

unread,
Nov 10, 2009, 3:37:20 PM11/10/09
to


Oh, I don't mean the \else above, but the one in the token register

\toks0=\expandafter\expandafter\expandafter{\reverse{abc\foo def\bar
ghi\else1}}

If you do

\the\toks0

you have unmatched \else.

Donald Arseneau

unread,
Nov 10, 2009, 8:46:09 PM11/10/09
to
On Nov 10, 9:01 am, Pluto <a.m...@rocketmail.com> wrote:
> Please how can the \else be expanded without \fi after \@firstoftwo?
I haven't followed where this discussion went off to, but in the
original problem the dangling \fi wasn't necessary. Change the
\if0 ... \fi to \romannumeral-`0...

Ulrich D i e z

unread,
Nov 11, 2009, 5:33:25 AM11/11/09
to
Pluto wrote:

> Oh, I don't mean the \else above, but the one in the token register
>
> \toks0=\expandafter\expandafter\expandafter{\reverse{abc\foo def\bar ghi\else1}}
>
> If you do
>
> \the\toks0
>
> you have unmatched \else.

Sure. That's why I didn't do that.
Instead I did \showthe\toks0. ;-)

I didn't intend to get something into the token-register
that makes sense.
I just wanted to exhibit that combinations of \if.., \else or \fi-tokens
won't confuse/break the reversing-process any more.

For sor some obscure reason you could do something
like:

\toks0=\expandafter\expandafter\expandafter{\reverse{abc\foo def\bar ghi\else1}}

[...]
\if 0\the\toks0 hey the previous stuff is reversed!\fi


By the way: With the approach presented on the
TeX-pearls-site you would get messages into the
log-file about \end occuring while \if is incomplete
if you did _not_ \the\toks0.

That's because the \if would be expanded but the
matching \fi which goes into the token-register
would not be expanded.

Sincerely

Ulrich

Ulrich D i e z

unread,
Nov 11, 2009, 5:34:02 AM11/11/09
to
Donald Arseneau wrote:

Afaik `0 is a character-constant which means that `0 is
an explicitely stated number.

With a construct like

\romannumeral
<character-constant>%
<decimal digits or something that expands to decimal digits>

, the part <decimal digits or something that expands to decimal digits>
will not be considered part of the numerical-value that is to be
converted into roman-notation while while with a construct like

\romannumeral
<decimal digits or something that expands to decimal digits>%
<decimal digits or something that expands to decimal digits>

both <decimal digits or something that expands to decimal digits>
will be considered part of the numerical-value that is to be converted
into roman-notation.

But both with character-constants and other explicitely stated
numbers, \romannumeral will keep expanding until it is clear
whether a space-token follows that explicitely stated number
and if so, then it will consume that space.

Even with character-constants, \romannumeral will also
expand the �"\noexpand" and then consume the <space-token>
from a trailing sequence "\noexpand<space-token>".


Therefore my last suggestion
(in news:hd1mnm$1bu$02$1...@news.t-online.com )
was:

\newcommand\reverse[1]{\romannumeral\reverseX{}#1{ }0\stopreverse}
\newcommand*\stopreverse[1]{\noexpand\stopreverse}
\makeatletter
\newcommand\reverseX[2]{%
\ifx\stopreverse#2%
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{#1}%
{\reverseX{#2#1}}%
}
\makeatother


\reverse reverses "argumentwise" (not token-wise!)
while \reverseX takes undelimited arguments which
means that it removes space-tokens. That's why I put
the space before the "0" into curly braces.

When the \reverseX-recursion is done, you get
something like

\romannumeral0<space-token>reversed stuff

and that will definitely terminate \romannumeral0-expansion
which in turn won't deliver any token and also won't
erroneously consume/expand trailing \noexpand- or space-tokens
that were delivered as parts of the argument of \reverse.

Sincerely

Ulrich

Heiko Oberdiek

unread,
Nov 11, 2009, 11:34:30 PM11/11/09
to
Ulrich D i e z <eu_an...@web.de> wrote:

> \newcommand\reverse[1]{\romannumeral\reverseX{}#1{ }0\stopreverse}
> \newcommand*\stopreverse[1]{\noexpand\stopreverse}
> \makeatletter
> \newcommand\reverseX[2]{%
> \ifx\stopreverse#2%
> \expandafter\@firstoftwo
> \else
> \expandafter\@secondoftwo
> \fi
> {#1}%
> {\reverseX{#2#1}}%
> }
> \makeatother

You can save one call of \reverseX:


\newcommand\reverse[1]{\romannumeral\reverseX{}#1{0 }\stopreverse}

or
\newcommand\reverse[1]{\romannumeral0\reverseX{}#1{ }\stopreverse}

And another one by

\newcommand\reverse[1]{\romannumeral0\reverseX{}#1\stopreverse}
[...]


\newcommand\reverseX[2]{%
\ifx\stopreverse#2%
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{ #1}%
{\reverseX{#2#1}}%
}

Yours sincerely
Heiko <ober...@uni-freiburg.de>

Donald Arseneau

unread,
Nov 12, 2009, 2:18:14 AM11/12/09
to
On Nov 11, 2:34 am, Ulrich D i e z <eu_angel...@web.de> wrote:
> Donald Arseneau wrote:
> > On Nov 10, 9:01 am, Pluto <a.m...@rocketmail.com> wrote:
> > > Please how can the \else be expanded without \fi after \@firstoftwo?
> > I haven't followed where this discussion went off to, but in the
> > original problem the dangling \fi wasn't necessary. Change the
> > \if0 ... \fi to \romannumeral-`0...
>
> Afaik `0 is a character-constant which means that `0 is
> an explicitely stated number.
...

> Even with character-constants, \romannumeral will also
> expand the ´"\noexpand" and then consume the <space-token>
> from a trailing sequence "\noexpand<space-token>".

Yes, it has the effect of consuming a space if it sees one
before seeing another token (but it does not run away
consuming any other tokens). Since the original version
removed spaces anyway, I assumed that it was not a
problem. It is good that you have been making the reversal
more robust.

DA

0 new messages