Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

Just when I think I starting to understand \expandafter I run into this problem where I am trying to test if \ConditionG, \ConditionH, etc are defined in a loop. I tried all the combinations I could think of and am not able to get this to work:

\documentclass{article}
\usepackage{pgffor}
\usepackage{etoolbox}

\begin{document}

%\def\ConditionG{}% This should show up as undefined
\def\ConditionH{}

\foreach \i in {G, H}{
    \expandafter\ifdefined\csname Condition\i\endcsname% Incorrect Results: everything defined
        \textbackslash Condition\i\ is defined\par
    \else
        \textbackslash Condition\i\ is not defined\par
    \fi
}

\hrule
\foreach \i in {G, H}{% Using etoolbox
    \ifdef{\expandafter\csname Condition\i\endcsname}{% Incorrect Results: everything defined
        \textbackslash Condition\i\ is defined\par
    }{
        \textbackslash Condition\i\ is not defined\par
    }
}

\end{document}

Joseph's detailed explanation regarding When to use \edef, \noexpand, and \expandafter? makes sense to me, but can't seem to apply that to my problem.

share|improve this question
add comment

3 Answers

up vote 6 down vote accepted

You should use \ifcsname ... \endcsname instead.

Note that \csname ... \endcsname makes the undefined control sequence to be \relax. See TeXbook Exercise 7.7:

When \csname is used to define a control sequence for the first time, that control sequence is made equivalent to \relax until it is redefined.

You can test a macro like this:

\documentclass{article}
\usepackage{pgffor}
\usepackage{etoolbox}

\begin{document}

\def\ConditionH{}

\foreach \i in {G, H}{
    \ifcsname Condition\i\endcsname
        \textbackslash Condition\i\ is defined\par
    \else
        \textbackslash Condition\i\ is not defined\par
    \fi
}

\hrule
\foreach \i in {G, H}{% Using etoolbox
    \ifcsdef{Condition\i}{%
        \textbackslash Condition\i\ is defined\par
    }{
        \textbackslash Condition\i\ is not defined\par
    }
}

\end{document}
share|improve this answer
1  
And without eTeX, people often use \expandafter\ifx\csname foo\endcsname\relax ... \fi to test a macro. In LaTeX2e, this is what \@ifundefined do. –  Leo Liu Jun 21 '11 at 5:55
1  
As I say in my answer, the approach of \@ifundefined works expandably but fills up the hash table, whereas using a group avoids the hash table issue at the cost of not being expandable. –  Joseph Wright Jun 21 '11 at 7:19
1  
@Joseph: in fact, I realized that even \newcount\i \def\test{\advance\i by 1{\csname foo\the\i\endcsname}\test}\test fills the hash table (and apparently, the control sequence is put in the hash table even when scanned within a \def (or any token list). –  Bruno Le Floch Jun 23 '11 at 23:08
add comment

In addition to Leo'a answer, without e-TeX the usual test for an undefined control sequence is

\def\ifundefined#1{%
  \begingroup\expandafter\expandafter\expandafter\endgroup
  \expandafter\ifx\csname #1\endcsname\relax
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
}

This works by doing the \csname ... \endcsname inside a group and testing if the meaning of the name we want is equal to \relax. The group means that if the control sequence was undefined then it is only made into \relax within the test, and not globally. The downside is that this test is not expandable. Of course, it cannot tell the difference between an undefined control sequence and one that is deliberately equal to \relax. (The \expandafters here make the \csname happen before the \ifx, which itself takes place before the \endgroup.)

A side note. In the LaTeX2e kernel, \@ifundefined does not use the grouping approach, so that it has an expandable test. That means that each time you use \@ifundefined with a previously-undefined control sequence, it does end up defined as \relax.

share|improve this answer
1  
You can also write this a little shorter by placing the \endgroup commands in the clauses: \begingroup \expandafter\ifx\csname #1\endcsname\relax \endgroup \expandafter\@firstoftwo \else \endgroup \expandafter\@secondoftwo \fi –  Martin Scharrer Jun 21 '11 at 10:03
add comment

In addition to the other, more relevant answers, I want to mention that your second code block uses \expandafter redundantly. The result of \expandafter\csname Condition\i\endcsname is just \csname Condition\i\endcsname, since the "C" is expanded before \csname. As it turns out, \csname itself continues the expansion and eventually gets \i, but for example something like \expandafter\toks0={\i} will backfire (it expands the 0, not the \i).

It's possible you intended to use \expandafter as an "execute this" command, as though its function were: \expandafter\a\b first expands \b, then expands \a. In fact, all it does is expand \b "in place", without touching \a; if \a does expand, that is because TeX's own "mouth" continues processing the input, expanding as it goes. If \a is not expandable but executable, then it is executed, unless that is inhibited, like in \edef. For example, \edef\macro{\def\a{a}\a} does not define \macro=a, but rather gives an error (assuming \a was not defined already) since it doesn't expand \def and thus, doesn't know that the \a after it is not intended to be expanded. No amount of \expandafters will change this.

If that was not your intention, please take my lecture to be directed at the audience.

share|improve this answer
    
Thanks for pointing that out. Am hoping that one of these days I can fully comprehend \expandafter. –  Peter Grill Jun 22 '11 at 0:10
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.