[Posted to info-tex on 25 Oct 91; see answer.001] %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "*** Exercise 2 (hard): "Define an "ifempty" macro that takes one argument and resolves "essentially to \iftrue if the argument is empty, and \iffalse "otherwise. This is useful for handling arguments given by "users to commands defined in a macro package such as LaTeX. " "Plain TeX or LaTeX-style solutions are both acceptable, that "is, " " \ifempty{...}TRUE CASE\else FALSE CASE\fi " "or " " \ifempty{...}{TRUE CASE}{FALSE CASE} The LaTeX-style solution that I had prepared was, I thought, pretty good, but Donald Arseneau observed that it fails the test \test{{\iftrue a\else b\fi}}, which was not in my list of tests. >>Solution 1 [mine] \catcode`\@=11 % \@car is actually already defined in latex.tex, but for % maximum robustness it needs to have the \long prefix: \long\def\@car#1#2\@nil{#1} \long\def\@first#1#2{#1} \long\def\@second#1#2{#2} \long\def\ifempty#1{\expandafter\ifx\@car#1@\@nil @\@empty \expandafter\@first\else\expandafter\@second\fi} \catcode`\@=12 \long\def\test#1{\begingroup \toks0{[#1]}% \newlinechar`\/\message{/\the\toks0: \ifempty{#1}{EMPTY}{NOT empty}% }\endgroup} >>EndSolution The advantage of using the auxiliary macros \@first and \@second, together with the \expandafter's, is that it allows the true and/or false cases to end with arbitrary things, even macros that require arguments that have not yet been read (any number of arguments, even delimited arguments). From here it is easy to implement an \ifnotempty test that has a null false case. This is often useful in dealing with user-supplied arguments: `If #1 is empty, do nothing; otherwise, do the following with #1: ...' \long\def\ifnotempty#1{\ifempty{#1}{}} Don Arseneau came up with a plain TeX style solution, using an ingenious device with \then to pass the test case \expandafter\iffalse\test{x}\fi. The comments in the solution are his. >>Solution 1 [Donald Arseneau]: % \ifblank{...}\then Test if a parameter is blank (null or spaces). % Use the inaccessable "letter" @ to separate parameters. The two cases are: % _text_is_not_blank_ _text_is_blank_ % #1<- whatever #1<-@ % #2<- whatever (possibly null) #2<- % #3<- @ #3<-. % #4<- .. #4<-. % \if @.. {false} \if .. {true} % In the {false} case, the extra period is skipped so it doesn't hurt. \catcode`\@=11 % as in plain.tex \let\then\iftrue \long\def\ifblank#1\then{\Ifbl@nk#1@@..\then}% \long\def\Ifbl@nk#1#2@#3#4\then{\if#3#4} \catcode`\@=12 \long\def\test#1{\begingroup \toks0{[#1]}% \newlinechar`\/\message{/\the\toks0: \ifblank{#1}\then EMPTY\else NOT empty\fi% }\endgroup} >>EndSolution The good thing about this solution is that it doesn't subject any part of the user-supplied argument to the \ifx test. Using @ with category code of 11 as a delimiter for the user-supplied text is extremely safe because even in internal code @ doesn't appear by itself, only as part of control sequence names. In a partial solution, Peter Schmitt pushed the idea a little further by using space with category code 3 as the delimiter. There is another way of handling the problematic \iffalse test, in a plain-TeX style solution, by using a suggestion of Donald Knuth that appeared in TeXhax a while ago, in reply to a query of Stephan von Bechtolsheim (texhax89, #38 (post from svb, 17 Apr 89)). >>Solution 3 [Arseneau/Knuth]: % Usage: \if\blank{#1}...\else...\fi \catcode`\@=11 % as in plain.tex \long\def\blank#1{\bl@nk#1@@..\bl@nk}% \long\def\bl@nk#1#2@#3#4\bl@nk{#3#4} \catcode`\@=12 \long\def\test#1{\begingroup \toks0{[#1]}% \newlinechar`\/\message{/\the\toks0: \if\blank{#1}EMPTY\else NOT empty\fi% }\endgroup} >>EndSolution At the end of Exercise 2 I wrote: "The two tests on the first line should produce a message "EMPTY" and "the remaining ones, "NOT empty". The reason for saying that the second "test should return "EMPTY" is that (1) this is the ideal behavior for "the applications I've encountered so far; (2) at least one other person "working independently arrived before me at a solution essentially "identical to mine, including this behavior. The details and credit to "the other guy will be given at solution time. The name of the `other guy' is Michael Wester; a listing of his macros was published in the preprints for the July 1991 TUG meeting in Dedham, Massachusetts (`Form Letter in LaTeX with 3-across Mailing Labels Capability', joint paper with Jackie Damrau). In rereading the preprint recently, it seems to me the presentation is more different from Exercise 2 and its solutions than I had previously imagined, but the essential ideas are there. See \wcar, \wcdr and related macros. By the way, if anyone came up with a fully expandable test (suitable for use inside a \message) for which \test{ } came up false instead of true, I would be interested to hear about it. I didn't mean to eliminate that possibility in my original statement of the problem.