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

Macro puzzle: maximally general \ifempty

55 views
Skip to first unread message

Robert R Schneck

unread,
Jun 13, 2003, 10:29:26 AM6/13/03
to
I have found that TeX macros provide some entertaining brainteasers.
Recently I've been amusing myself with trying to develop an \ifempty
macro, where
\ifempty{}{<true stuff>}{<false stuff>}
expands to <true stuff> and
\ifempty{<any tokens>}{<true stuff>}{<false stuff>}
expands to <false stuff>.

This question has been discussed here before, but as far as I can tell
all the solutions have depended on having some sort of sentinel which
one assumes does not occur in the argument to be tested for emptiness.
It's not hard to create sentinels which are good enough for any
useful task---for instance, the same character several times with
different category codes---but I'm interested, just as an intellectual
challenge, in an \ifempty which can work on *any* possible argument.
So you can assume that the tested tokens are brace-balanced and have no
\outer macros. That, I think, is it.

If someone has developed such a thing before, let me know! Otherwise
I'll work out a solution I have in mind, and post it here after giving
others a chance to puzzle it out for a bit.

Best,
Robert
--


-----= Posted via Newsfeeds.Com, Uncensored Usenet News =-----
http://www.newsfeeds.com - The #1 Newsgroup Service in the World!
-----== Over 80,000 Newsgroups - 16 Different Servers! =-----

Jean-Pierre Drucbert

unread,
Jun 13, 2003, 10:45:46 AM6/13/03
to
Look in the minitoc.sty package, the internal macros \mtc@CkFile and
\mtc@CkStr test if a file ou a string is empty (in fact, \mtc@CkStr
writes the string into a file and tests the file). The result is
tested by \if@mtc@FE. The besat is not trivial, but seems working.

Regards,

Jean-Pierre

--
Jean-Pierre F. Drucbert (JPFD) Email: drucbert at onecert.fr
ONERA/Centre de Toulouse SRI Tél. 05-62-25-25-15; FAX: 05-62-25-25-35
Office national d'études et de recherches aérospatiales
Centre de Toulouse Service réseaux et informatique
Complexe scientifique de Rangueil
2, Avenue Édouard Belin BP 4025 F-31055 TOULOUSE CEDEX FRANCE

A bird cannot land once on a great tree and claim to know it.
But I imagine that he would, yes.
Iain M. Banks (1993), Against a dark background.

Robert R Schneck

unread,
Jun 13, 2003, 1:23:50 PM6/13/03
to
Jean-Pierre Drucbert <druc...@atoll.onecert.fr> wrote:
> In article <3ee9d...@corp.newsgroups.com>, Robert R Schneck
> <sch...@math.berkeley.edu> writes:
>|> Recently I've been amusing myself with trying to develop an \ifempty
>|> macro, where
>|> \ifempty{}{<true stuff>}{<false stuff>}
>|> expands to <true stuff> and
>|> \ifempty{<any tokens>}{<true stuff>}{<false stuff>}
>|> expands to <false stuff>.
>
> Look in the minitoc.sty package, the internal macros \mtc@CkFile and
> \mtc@CkStr test if a file ou a string is empty (in fact, \mtc@CkStr
> writes the string into a file and tests the file). The result is
> tested by \if@mtc@FE. The besat is not trivial, but seems working.

Writing to a file is not something I had considered.
Unfortunately, since macros are expanded by \write, this assumes that
the tested tokens behave well under expansion (for example, no
"\def\infiniteloop{\infiniteloop}". Also, if the tested tokens are
all characters with the character code of ' ', and any category codes,
it will look "empty" to this mechanism.

Also, I would like an \ifempty that works by macro expansion only; so
that you could put it in an \edef and get the correct expansion.

Incidentally, I intend that even ordinary blank spaces are NOT empty.
But if someone has a general "\ifblank" which counts both {} and { },
then \ifempty could be written something like:
\def\ifempty#1{\ifblank{#1}
{\ifx!#1!\expandafter\firstoftwo\else\expandafter\secondoftwo\fi}
{\secondoftwo}}

Donald Arseneau

unread,
Jun 13, 2003, 11:33:37 PM6/13/03
to
Robert R Schneck <sch...@math.berkeley.edu> writes:

> all the solutions have depended on having some sort of sentinel which
> one assumes does not occur in the argument to be tested for emptiness.
> It's not hard to create sentinels which are good enough for any
> useful task---for instance, the same character several times with
> different category codes---but I'm interested, just as an intellectual
> challenge, in an \ifempty which can work on *any* possible argument.
> So you can assume that the tested tokens are brace-balanced and have no
> \outer macros. That, I think, is it.

Trivial:

\outer\def\mySentinelToken{}


Donald Arseneau as...@triumf.ca


Donald Arseneau

unread,
Jun 14, 2003, 12:08:32 AM6/14/03
to
druc...@atoll.onecert.fr (Jean-Pierre Drucbert) writes:

> \mtc@CkStr test if a file ou a string is empty (in fact, \mtc@CkStr
> writes the string into a file and tests the file).

Why???!! It sounds crazy!

Your tests for macros being totally empty would be as simple as
\ifx\mtctitle\@empty

If you want to test for blank using "\mtc@ifmtarg" you have
to use:
\expandafter\mtc@ifmtarg\expandafter{\mtctitle}

(This uses a sentinel of Q_3, and it's uses in minitoc
don't require expandability.)


Donald Arseneau as...@triumf.ca

Robert R Schneck

unread,
Jun 14, 2003, 5:44:36 AM6/14/03
to

I had been hoping to get something like that to work.

But if I try
\def\ifempty#1{\ifx\mySentinelToken#1\mySentinelToken


\expandafter\firstoftwo\else\expandafter\secondoftwo\fi}

well, even making sure that I define \ifempty before \mySentinelToken
do that the outerness doesn't choke the definition, I get something that
doesn't work on non-empty input: outer tokens can't appear in the
skipped tokens of a conditional.

And outer tokens don't work as macro argument delimiters, either.

Did you have something else in mind?

Donald Arseneau

unread,
Jun 14, 2003, 9:54:57 PM6/14/03
to
Robert R Schneck <sch...@math.berkeley.edu> writes:

> Donald Arseneau <as...@triumf.ca> wrote:
> > \outer\def\mySentinelToken{}


>
> outer tokens can't appear in the
> skipped tokens of a conditional.
> And outer tokens don't work as macro argument delimiters, either.

I spoke too soon.

Note that the test \ifx S#1S (for sentinel S) is not safe (for
reasons mentioned in the Around-the-bend discussions).

Donald Arseneau as...@triumf.ca

Robert R Schneck

unread,
Jun 16, 2003, 6:08:50 PM6/16/03
to
I had had a terrifically complicated solution, but I'm very pleased that
over the weekend I was able to figure out a way to condense a similar
idea into a rather small macro. I'd appreciate being told if it doesn't
work, or any advice on how to improve it.

\long\def\gobble#1{}
\long\def\firstoftwo#1#2{#1}
\long\def\secondoftwo#1#2{#2}

\long\def\ifempty#1{%
\expandafter\gobble\expandafter{\expandafter{\string#1{}}%
\expandafter\gobble\expandafter{\expandafter{\string#1!}%
\expandafter\secondoftwo\expandafter{\expandafter{\iffalse}}\fi}%
\expandafter\firstoftwo\expandafter{\iffalse}\fi\firstoftwo}%
\secondoftwo}

Explanation: we apply \string to the first token of ``#1{}'', and then
we test whether we have ``destroyed'' an explicit begin-group character.
If not, we know that #1 is non-empty, and we end up at the
\secondoftwo on the last line. If we did destroy a begin-group
character, we run the same test on ``#1!''. Now, if we destroyed
a begin-group character *again*, #1 is non-empty and we end up on
line 3 of the macro. If this time we did not destroy a begin-group
character, #1 must be empty and we end up on line 4 of the macro.
Lines 3 and 4 do some work to get rid of extra braces and end up
as \secondoftwo and \firstoftwo.

Again, this \ifempty considers explicit blank spaces also as
non-empty; this might have been called ``\ifnull''. If you want an
\ifblank which ignores explicit blank spaces:

\long\def\ifblank#1{\expandafter\ifempty\expandafter{\gobble#1!}}

Donald Arseneau

unread,
Jun 16, 2003, 7:45:21 PM6/16/03
to
Robert R Schneck <sch...@math.berkeley.edu> writes:

> \long\def\ifempty#1{%
> \expandafter\gobble\expandafter{\expandafter{\string#1{}}%

Neat idea!

> \expandafter\gobble\expandafter{\expandafter{\string#1!}%

And you nicely handled the the case of argument beginning with {

However...

It still hits the argument with \string, and it still skips/scans over
the argument, so it fails with "\ifempty{\if...\fi}" It was the discussion
of this that is most relevant in the around-the-bend discussion.

Something I thought about before, but did not recommend because it
also suffers from this, is to use } as the salient delimiter:
"\ifx }#1}" because "}" can never appear as the first token in a macro
parameter. It needs some brace restoration, but you are obviously
up to that complication.

You could also use it with your \string method:


\expandafter\gobble\expandafter{\expandafter{\string#1}}%

and avoid the need for the second \string#1! test.

I think the problems you get from subjecting part of the argument
to \ifx (or \string) are worse than the possibility of something else
using the sentinel token. I would be very interested (and, at this
point, surprised) to see a sentinel-free expandable empty test, safe
for all arguments.

Donald Arseneau as...@triumf.ca

Robert R Schneck

unread,
Jun 16, 2003, 8:29:59 PM6/16/03
to
Donald Arseneau <as...@triumf.ca> wrote:
> Robert R Schneck <sch...@math.berkeley.edu> writes:
[an attempt to create a sentinel-free expandable empty test]

>
> However...
>
> It still hits the argument with \string, and it still skips/scans over
> the argument, so it fails with "\ifempty{\if...\fi}" It was the discussion
> of this that is most relevant in the around-the-bend discussion.

Now, it skips over the argument as a macro argument, *not* using
primitive \if... scanning. So
\ifempty{\if...\fi} {\message{empty}} {\message{non-empty}}
does yield the desired result.

However, this does mean that one may not be able to use this kind of
\ifempty inside of a primitive \if.... Is that what you mean?


> Something I thought about before, but did not recommend because it
> also suffers from this, is to use } as the salient delimiter:
> "\ifx }#1}" because "}" can never appear as the first token in a macro
> parameter. It needs some brace restoration, but you are obviously
> up to that complication.

Unfortunately implicit end-groups like \egroup count for such an \ifx.


> I think the problems you get from subjecting part of the argument
> to \ifx (or \string) are worse than the possibility of something else
> using the sentinel token. I would be very interested (and, at this
> point, surprised) to see a sentinel-free expandable empty test, safe
> for all arguments.

Certainly you are right about subjecting the argument to an \ifx.
But I'm not certain what the problem is with subjecting it to \string?

Donald Arseneau

unread,
Jun 16, 2003, 11:43:34 PM6/16/03
to
Robert R Schneck <sch...@math.berkeley.edu> writes:

> Donald Arseneau <as...@triumf.ca> wrote:
> > Robert R Schneck <sch...@math.berkeley.edu> writes:
> [an attempt to create a sentinel-free expandable empty test]
> >

> > It still hits the argument with \string, and it still skips/scans over
> > the argument, so it fails with "\ifempty{\if...\fi}"
>

> Now, it skips over the argument as a macro argument, *not* using
> primitive \if... scanning.

Excellent!

Thinking about it more, I also see difficulties with the variant
\gobble{\string#1} I had mentioned -- it doesn't make matters simpler,
and probably much more difficult. Yes, I think touching { with
\string is the right thing.

Hey! This is even correct with the nastiest case I could think of!
\escapechar=-1
\expandafter\rsempty\expandafter{\csname\endcsname}{Yes}{No}
When TeX performs \string on the null-string command, it gives the
text "csnameendcsname" instead of the expected nulkl string ""!

Donald Arseneau as...@triumf.ca

Robert R Schneck

unread,
Jun 17, 2003, 9:42:50 AM6/17/03
to
Donald Arseneau <as...@triumf.ca> wrote:
> Robert R Schneck <sch...@math.berkeley.edu> writes:
>> Donald Arseneau <as...@triumf.ca> wrote:
>> > Robert R Schneck <sch...@math.berkeley.edu> writes:
>> [an attempt to create a sentinel-free expandable empty test]
>> >
>> > It still hits the argument with \string, and it still skips/scans over
>> > the argument, so it fails with "\ifempty{\if...\fi}"
>>
>> Now, it skips over the argument as a macro argument, *not* using
>> primitive \if... scanning.
>
> Excellent!
>
> Thinking about it more, I also see difficulties with the variant
> \gobble{\string#1} I had mentioned -- it doesn't make matters simpler,
> and probably much more difficult. Yes, I think touching { with
> \string is the right thing.

Actually, I think you *were* right about this idea:

\long\def\gobble#1{}
\long\def\firstoftwo#1#2{#1}
\long\def\secondoftwo#1#2{#2}

\long\def\ifempty#1{%
\iffalse{\fi%


\expandafter\gobble\expandafter{\expandafter{\string#1}%

\expandafter\gobble\string}%
\expandafter\firstoftwo\expandafter{\iffalse}\fi\secondoftwo}%
\firstoftwo}

After the \gobble, we end up on line 3 if #1 begins with {,
on line 4 if #1 begins with anything else, and on line 5 if
#1 is empty.

I'm pleased that this variant doesn't have to duplicate the argument.
It's also very short. So, excellent idea!

It even has the property that the argument is hit with \string after 3
expansions, then disappears after 4. So if you did want to use this
inside of an \if...\fi, and could ensure that the argument is \if-\fi
balanced, it is safe to skip over unless it ended up being expanded
exactly 3 times... which seems very unlikely.

Speaking of which,
\ifempty{...}{\iftrue}{\iffalse} ... \else ... \fi
might be a useful idiom. (Though it's not \if-\fi balanced itself...
replace that \iftrue with \expandafter\iftrue\gobble\fi---better, use
your idea of a argument delimiter \then which is \let-equal to an \if.)

> Hey! This is even correct with the nastiest case I could think of!
> \escapechar=-1
> \expandafter\rsempty\expandafter{\csname\endcsname}{Yes}{No}
> When TeX performs \string on the null-string command, it gives the
> text "csnameendcsname" instead of the expected nulkl string ""!

Which seems silly, but I'm glad it works. (If it hadn't, \meaning
instead of \string probably would have.)

Dan Luecking

unread,
Jun 17, 2003, 3:56:24 PM6/17/03
to
On 16 Jun 2003 17:08:50 -0500, Robert R Schneck
<sch...@math.berkeley.edu> wrote:

>I had had a terrifically complicated solution, but I'm very pleased that
>over the weekend I was able to figure out a way to condense a similar
>idea into a rather small macro. I'd appreciate being told if it doesn't
>work, or any advice on how to improve it.
>
>\long\def\gobble#1{}
>\long\def\firstoftwo#1#2{#1}
>\long\def\secondoftwo#1#2{#2}
>
>\long\def\ifempty#1{%
> \expandafter\gobble\expandafter{\expandafter{\string#1{}}%
> \expandafter\gobble\expandafter{\expandafter{\string#1!}%
> \expandafter\secondoftwo\expandafter{\expandafter{\iffalse}}\fi}%
> \expandafter\firstoftwo\expandafter{\iffalse}\fi\firstoftwo}%
> \secondoftwo}

It is possible I am missing something (is it likely DA could be
wrong?) but after the first three \expandafter-s expand the first
\string you have only:
\gobble{<...brace balanced code...>}\secondoftwo
which would appear to always give the second option.

Is there any context where this could possibly work?


Dan

--
Dan Luecking Department of Mathematical Sciences
University of Arkansas Fayetteville, Arkansas 72701
luecking at uark dot edu

Robert R Schneck

unread,
Jun 17, 2003, 5:43:09 PM6/17/03
to
Dan Luecking <Look...@uark.edu> wrote:
> On 16 Jun 2003 17:08:50 -0500, Robert R Schneck
><sch...@math.berkeley.edu> wrote:
[apparently, an expandable sentinel-free empty test]

>
> It is possible I am missing something (is it likely DA could be
> wrong?) but after the first three \expandafter-s expand the first
> \string you have only:
> \gobble{<...brace balanced code...>}\secondoftwo
> which would appear to always give the second option.
>
> Is there any context where this could possibly work?

The trick is, that the string may have turned a begin-group character {
into an ordinary character {; then the code is no longer brace-balanced,
and the gobble puts you in the middle of it.

0 new messages