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.
The following throws an "Undefined control sequence" error:

\edef\testa{\edef\testb{foobar}\testb}

when it gets to expanding \testb. This problem is also mentioned in "TEX by Topic" 12.6.2. But no solution is given.

EDIT2, this is a minimal not-working example:

\documentclass{article}

\newenvironment{definition}[1]{\def\currentdef{#1}}{}

\makeatletter

\newcommand\contains[2]{%
    \edef\listarg{#1}%
    \xdef\needle{#2}%
    \expandafter\contains@sub\listarg,\relax\noexpand\@eolst%
}
\def\contains@sub#1,#2\@eolst{%
    \edef\match{#1}%
    \ifx\needle\match%
        true
    \else
        \ifx\relax#2\relax\else
            \contains@sub#2\@eolst%
        \fi
    \fi}

\newcommand\cond[1]{%
    \edef\test{\contains{\currentdef}{#1}}%
    \ifx\relax\test\relax\def\ommitthis\relax\fi%
    \@ifundefined{ommitthis}{#1}{}%
}

\makeatother

\def\test{test2}

\begin{document}

\begin{definition}{test2,test3}
    \cond{test3}
    \cond{\test}
    \cond{test1}
\end{definition}

\end{document}

\cond should check whether the given argument was listed in the surrounding definition. So test2 and test3 must be ommited and only test1 should be printed.

share|improve this question
4  
If you want the definition of \testb to be executed while expanding the definition of \testa, that's impossible. If you want the definition of \testa to contain the definition statement for \testb, then use \noexpand\testb (twice). –  Stephan Lehmke Jun 14 '12 at 17:07
    
Actually I want the definition to be executed during the expansion. For some more context: I wrote a macro that tests whether one argument is contained in a ,-list given as the other argument. Now I'm using this macro in a \edef somewhere so that I can test the result with \ifx. But unfortunately the macro uses \edef itself... –  canaaerus Jun 14 '12 at 17:16
4  
@canaaerus That's still not enough context. You cannot use an \edef inside another \edef, but to suggest an alternative approach we need to know what is happening in \contains, etc. Please post a full example, starting \documentclass and ending \end{document}. –  Joseph Wright Jun 14 '12 at 17:35
6  
\edef isn't expandable, so it's not expanded within an \edef! –  Hendrik Vogt Jun 14 '12 at 17:35
    
Well I thought maybe it is possible to write the test part without edef, thus avoiding the problem. But of course I can post the contains thing too. –  canaaerus Jun 14 '12 at 17:38

1 Answer 1

up vote 7 down vote accepted

You just need to cycle through the list given as argument:

\documentclass{article}

\newenvironment{definition}[1]
  {\def\currentdef{#1}}
  {}

\makeatletter
\def\cond#1{\edef\@tempa{#1}%
  \@tempswafalse
  \@for\@tempb:=\currentdef\do
    {\ifx\@tempa\@tempb\@tempswatrue\fi}%
  \if@tempswa\else\@tempa\fi
}
\makeatother

\begin{document}

\def\test{test2}

\begin{definition}{test2,test3}
    \cond{test3}
    \cond{\test}
    \cond{test1}
\end{definition}

\end{document}

The definition environment stores the argument in the macro \currentdef (as you do). The macro \cond expands completely its argument and stores the result in \@tempa. Then sets the scratch conditional \@tempswa to false and, with the help of \@for compares \@tempa with each part of \currentdef; the block

\@for\@tempb=\LIST\do{<code>}

where \LIST expands to a,b,c (any comma separated list of tokens) executes <code> with \@tempb expanding to a, b and c in turn.

The <code>, in our case, is "check if \@tempa and \@tempb have the same meaning (expansion, here) and, if so, set \if@tempswa to true.

Finally \cond tests \@iftempswa: if it's true nothing else is done, otherwise \@tempa is expanded.


The same effect, but with even some improvements can be obtained with expl3:

\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentEnvironment{definition}{m}
 { \canaaerus_store:n { #1 } } { }
\NewDocumentCommand{\cond}{m}
 { \canaaerus_check:x { #1 } }

\cs_new_protected:Npn \canaaerus_store:n #1
 {
  \seq_set_split:Nnn \l_canaaerus_list_seq { , } { #1 }
 }

\cs_new_protected:Npn \canaaerus_check:n #1
 {
  \seq_if_in:NnF \l_canaaerus_list_seq { #1 } { #1 }
 }
\cs_generate_variant:Nn \canaaerus_check:n { x }

\seq_new:N \l_canaaerus_list_seq
\ExplSyntaxOff

The improvement is that spaces around the list are stripped off, so

\begin{definition}{test2 , test3 }

is just as good as the input without spaces.

The strategy is the same, but the argument to definition is stored in a sequence (as a bonus, spaces around the list argument are stripped off). The cycle works similarly as before: we check whether the argument to \canaaerus_check:x (completely expanded) appears in the sequence; if not, the argument is output.

Notice how we get the complete expansion: the macro defined is \canaaerus_check:n, but we create also a variant so that using

\canaaerus_check:x {<tokens>}

is thus pretty much equivalent to saying

\edef\@tempa{<tokens>}\expandafter\canaaerus_check:n\expandafter{\@tempa}

(I apologize for the horrible mixture of LaTeX3 and primitive syntax, but it's just by way of example). The convenience of the "variant" method should be self-evident.

share|improve this answer
    
That is certainly a lot simpler. Thank you! –  canaaerus Jun 14 '12 at 19:34

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.