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

Cath 22, strange TeX behaviour

6 views
Skip to first unread message

Danie Els

unread,
Sep 9, 2002, 2:46:19 AM9/9/02
to
The followings is a fairly common construction to test for an empty
optional argument. It should never test true for #1 anything but \empty
(or \@empty or maybe \relax). Strangely does #1=22 also test true?

\documentclass{minimal}
\newcommand*{\tst}[1][\empty]{%
\ifx#1\empty
[empty]~(#1)%
\else
[#1]~(#1)%
\fi}
\begin{document}
\noindent%
This is empty: \tst\\
This contains 21: \tst[21]\\
This contains 22: \tst[22]{} or does it?\\
This contains 23: \tst[23]
\end{document}

Yes I know the fix is to define, say: \def\temp{#1} and then test token
agains token with \ifx\temp\empty or use ifmtarg.sty, but nevertheless
should TeX never test true, irrespective of what the argument contains,
if it is not \empty!

This is not a LaTeX exclusive (in case it redefines some low-level TeX
command), TeX shows the same behavior:

\def\Empty{}
\def\TSTA#1{\ifx#1\Empty [EMPTY]\else#1\fi}
\def\TSTB#1{\ifx#1\relax [RELAX]\else#1\fi}
%
This is: \TSTA{\Empty}, this is \TSTA{21} and this is \TSTA{22}?\par
This is: \TSTB{\relax}, this is \TSTB{21} and this is \TSTB{22}?\par
\bye

I have tested it with the fpTeX distribution on TeXlive 7 TeX, Version
3.14159 (Web2c 7.3.7x) and the MiKTeX 2.2 distribution.

Can the TeX boffins please explain what is going on here?

--
Danie Els
(Substitude initials DNJ for first name in e-address)

Torsten Bronger

unread,
Sep 9, 2002, 3:40:45 AM9/9/02
to
Halloechen!

Danie Els wrote:

> The followings is a fairly common construction to test for an empty
> optional argument. It should never test true for #1 anything but \empty
> (or \@empty or maybe \relax). Strangely does #1=22 also test true?
>
> \documentclass{minimal}
> \newcommand*{\tst}[1][\empty]{%
> \ifx#1\empty
> [empty]~(#1)%
> \else
> [#1]~(#1)%
> \fi}
> \begin{document}
> \noindent%
> This is empty: \tst\\
> This contains 21: \tst[21]\\
> This contains 22: \tst[22]{} or does it?\\
> This contains 23: \tst[23]
> \end{document}
>
> Yes I know the fix is to define, say: \def\temp{#1} and then test token
> agains token with \ifx\temp\empty or use ifmtarg.sty, but nevertheless
> should TeX never test true, irrespective of what the argument contains,
> if it is not \empty!

I'm not sure, but maybe it compares the two *digits* which are
the same in the case of "22".

Tschoe,
Torsten.

Danie Els

unread,
Sep 9, 2002, 5:24:32 AM9/9/02
to

Torsten Bronger wrote:

You are absolutely right. The trick is to swap the test object around

\newcommand*{\tst}[1][\empty]{%

\ifx\empty #1\relax


[empty]~(#1)%
\else
[#1]~(#1)%
\fi}

--

Ulrike Fischer

unread,
Sep 9, 2002, 6:35:45 AM9/9/02
to
Danie Els <Dani...@sun.ac.za> schrieb:

>> I'm not sure, but maybe it compares the two *digits* which are
>> the same in the case of "22".
>>
>

> You are absolutely right. The trick is to swap the test object around
>
> \newcommand*{\tst}[1][\empty]{%
> \ifx\empty #1\relax
> [empty]~(#1)%
> \else
> [#1]~(#1)%
> \fi}
>

You should be aware that you are not testing if the argument is equal
to \empty but if is starting with \empty.

\tst[\empty 23]

will give "23[empty] (23)"

Ulrike Fischer

Robin Fairbairns

unread,
Sep 9, 2002, 6:57:08 AM9/9/02
to
Torsten Bronger <bro...@physik.rwth-aachen.de> writes:
>Danie Els wrote:
>> The followings is a fairly common construction to test for an empty
>> optional argument. It should never test true for #1 anything but \empty
>> (or \@empty or maybe \relax). Strangely does #1=22 also test true?
>>
>> \documentclass{minimal}
>> \newcommand*{\tst}[1][\empty]{%
>> \ifx#1\empty
>> [empty]~(#1)%
>> \else
>> [#1]~(#1)%
>> \fi}
>> \begin{document}
>> \noindent%
>> This is empty: \tst\\
>> This contains 21: \tst[21]\\
>> This contains 22: \tst[22]{} or does it?\\
>> This contains 23: \tst[23]
>> \end{document}
>
>I'm not sure, but maybe it compares the two *digits* which are
>the same in the case of "22".

indeed. danie is making the mistake of assuming that #1 is always a
single token: \ifx compares the two following tokens.

imo, it's not a cath 22 (whoever this person cath is ;-), but rather
danie's misunderstanding of the specification of the command.
--
Robin Fairbairns, Cambridge -- the man with no voice (_again_)

Danie Els

unread,
Sep 9, 2002, 7:33:50 AM9/9/02
to
Thank you everyone

Robin Fairbairns wrote:

>>>
>>I'm not sure, but maybe it compares the two *digits* which are
>>the same in the case of "22".
>>
>
> indeed. danie is making the mistake of assuming that #1 is always a
> single token: \ifx compares the two following tokens.


As a matter for the record, besides using ifmtarg.sty, is the safest
method to test for an optional argument according to Danie Els (whom was
proven wrong on numerous occasions):

\newcommand*{\tst}[1][\empty]{%

\def\temp{#1}%
\ifx\empty\temp
{... no opt arg ...}%
\else
{... with opt arg ...}%
\fi}


>
> imo, it's not a cath 22 (whoever this person cath is ;-), but rather
> danie's misunderstanding of the specification of the command.
>


cath??? I hope my youngsters (2nd and 4th grade) don't see it, I will
not hear the end of it for a long time :-)

Danie Els

unread,
Sep 9, 2002, 7:49:25 AM9/9/02
to

Danie Els wrote:

> Thank you everyone


>
> As a matter for the record, besides using ifmtarg.sty, is the safest
> method to test for an optional argument according to Danie Els (whom was
> proven wrong on numerous occasions):
>
> \newcommand*{\tst}[1][\empty]{%
> \def\temp{#1}%
> \ifx\empty\temp
> {... no opt arg ...}%
> \else
> {... with opt arg ...}%
> \fi}
>

Wrong again! this is getting out of hand (maybe someone can include it
in a FAQ of how NOT to post something on comp.text.tex).

Change the \def above to an \edef

\newcommand*{\tst}[1][\empty]{%

\edef\temp{#1}%


\ifx\empty\temp
{... no opt arg ...}%
\else
{... with opt arg ...}%
\fi}

--

Dan Luecking

unread,
Sep 9, 2002, 1:53:11 PM9/9/02
to
On Mon, 09 Sep 2002 13:49:25 +0200, Danie Els <Dani...@sun.ac.za>
wrote:

>
>Wrong again! this is getting out of hand (maybe someone can include it
>in a FAQ of how NOT to post something on comp.text.tex).
>
>Change the \def above to an \edef
>
> \newcommand*{\tst}[1][\empty]{%
> \edef\temp{#1}%
> \ifx\empty\temp
> {... no opt arg ...}%
> \else
> {... with opt arg ...}%
> \fi}

Now after
\def\x{\y\z}
\def\y{\z\z\z\z\z}
\def\z{\empty}
\tst[\x] will give

{... no opt arg ...}

Which may, or may not, be OK. It will also produce
an error message for \tst[\undefined], \tst[\if] and
other (probably unlikely) combinations.

If you are going to allow the optional argument to expand
(using an \edef) you could also use \if instead of \ifx and
omit the \edef:
\if!#1! ...
where "!" has to be some character that is never going to
show up at the start of #1 (even when expanded).

As has been said before, it all depends on what you mean by
"no opt arg". Does a space only count? How about \space? Does a
command that expands to nothing count? How about a command that
expands to that command? Does \relax count?

Most methods of checking for empty-ness (even \@ifmtarg) will
fail for some inputs or for some definitions of "empty". Try
googling. This has been discussed many times and (if memory
serves) ifmtarg.sty (and/or its current coding) was in response
to one such discussion.


Dan

--
Dan Luecking Department of Mathematical Sciences
University of Arkansas Fayetteville, Arkansas 72701

0 new messages