6

I get it when I write:

\expandafter\def\agent{Hello} 
\expandafter\def\expandafter\agent\expandafter{\agent World}  
\agent

result: Hello World

It doesn't work for me when I write:

\expandafter\def\csname agent1 \endcsname{Hello} 
\expandafter\def\expandafter\csname agent1\endcsname\expandafter{\csname agent1\endcsname World} 
\csname agent1 \endcsname

result:

I understand that I don't understand something with the use of the \expandafter extension, but I don't know what it is.

1
  • 4
    \expandafter\def\agent{Hello} looks wrong, what should \agent expand to in that case. I'd guess it's undefined at that point, so you'd get an error.
    – Skillmon
    Commented May 8 at 10:54

3 Answers 3

8

To expand a \csname to the underlying macro you need one step of expansion, to then expand that to the value you need another step of expansion. Also your \expandafter has to hit \csname, not the first token inside of it. \csname then expands everything until it finds \endcsname.

To get two steps of expansion you need three \expandafters (one to step over the second and execute the third, which will do the first step of expansion, then the second one remains and does the second step of expansion).

\def\agent{Hello}

\expandafter\def\expandafter\agent\expandafter{\agent\ World}
\meaning\agent

\expandafter\def\csname agent1\endcsname{Hello}
\expandafter\def\csname agent1\expandafter\expandafter\expandafter\endcsname\expandafter\expandafter\expandafter{\csname agent1\endcsname\ World}
\expandafter\meaning\csname agent1\endcsname

\bye
1
  • Skillmon, In the first option you are certainly right sorry, I wasn't paying attention For the second option, thank you very much, I succeeded))
    – Juli
    Commented May 8 at 12:23
3

With

\expandafter\def\expandafter\csname agent1\endcsname\expandafter{\csname agent1\endcsname World} 

you're actually redefining \csname, because the first \expandafter just triggers the second one, which does nothing because it tries to expand a, so you end up with \def\csname agent1\endcsname.

The usual trick for such cases is to use \expandafter (maybe multiple times) just before \endcsname, because TeX does full expansion during the processing of \csname.

Thus you can do

\expandafter\def\csname agent1\endcsname{Hello}

\expandafter\def\csname agent1\expandafter\expandafter\expandafter\endcsname
\expandafter\expandafter\expandafter{\csname agent1\expandafter\endcsname\space World}

\expandafter\show\csname agent1\endcsname

and TeX will stop with

> \agent1=macro:
->Hello World.

For the particular case, where \agent1 expands to characters, you can shorten to

\expandafter\def\csname agent1\endcsname{Hello}

\expandafter\edef\csname agent1\endcsname{\csname agent1\endcsname\space World}

\expandafter\show\csname agent1\endcsname

but of course this is not generally the same as appending something to the current meaning of a csname.

We can avoid the \expandafter orgy with token registers:

\expandafter\def\csname agent1\endcsname{Hello}

\toks0=\expandafter\expandafter\expandafter{\csname agent1\endcsname}
\toks2={ World}
\expandafter\edef\csname agent1\endcsname{\the\toks0 \the\toks2}

\expandafter\show\csname agent1\endcsname

\bye

or by abstracting the situation:

\def\appendtocsname#1#2{%
  \toks0=\expandafter\expandafter\expandafter{\csname#1\endcsname}%
  \toks2={#2}%
  \expandafter\edef\csname#1\endcsname{\the\toks0 \the\toks2}%
}

\expandafter\def\csname agent1\endcsname{Hello} 

\appendtocsname{agent1}{ World}

\expandafter\show\csname agent1\endcsname

\bye

Possibly less complicated is to use a scratch macro, \next is commonly used for such temporary purposes:

\def\appendtocsname#1#2{%
  \expandafter\let\expandafter\next\csname#1\endcsname
  \expandafter\def\csname#1\expandafter\endcsname\expandafter{\next#2}%
}

\expandafter\def\csname agent1\endcsname{Hello}

\appendtocsname{agent1}{ World}

\expandafter\show\csname agent1\endcsname

The console output will be the same as before.

This approach is needed if you want to prepend things, because in this case no (simple) sequence of \expandafter tokens would do.

With e-TeX extensions (pdftex, xetex, luatex), you can do it without scratch macros:

\def\appendtocsname#1#2{%
  \expandafter\edef\csname#1\endcsname{\expandcsonelevel{#1}\unexpanded{#2}}%
}
\def\expandcsonelevel#1{%
  \unexpanded\expandafter\expandafter\expandafter{\csname #1\endcsname}%
}

\expandafter\def\csname agent1\endcsname{Hello}

\appendtocsname{agent1}{ World}

\expandafter\show\csname agent1\endcsname

\bye

If you use LaTeX (but the infrastructure is also available for plain TeX, but needs e-TeX extensions), an altogether approach seems preferable.

\documentclass{article}
\ExplSyntaxOn

\NewDocumentCommand{\definecontainer}{mm}
 {
  \tl_clear_new:c { l__juli_container_#1_tl }
  \tl_set:cn { l__juli_container_#1_tl } { #2 }
 }

\NewDocumentCommand{\appendtocontainer}{mm}
 {
  \tl_put_right:cn { l__juli_container_#1_tl } { #2 }
 }

\NewExpandableDocumentCommand{\usecontainer}{m}
 {
  \tl_use:c { l__juli_container_#1_tl }
 }

\ExplSyntaxOff

\definecontainer{agent1}{Hello}

\begin{document}

\appendtocontainer{agent1}{ World}

\usecontainer{agent1}

\end{document}

Possibly \appendtocontainer should also check whether the named container already exists.

2

With your first example I doubt that the result is "Hello World":

  • You get different error messages on the console/in the .log-file due to the fact that \expandafter\def\agent in the first line triggers expanding \agent while \agent is undefined.
  • You get another error message on the console/in the .log-file due to the fact that the \expandafter-chain in the second line triggers expanding \agent while \agent is undefined.
  • The look of the code does not indicate that a space token might be intended to go between Hello and World. In the code there is a space character between \agent and World, but after tokenizing and appending to the token stream a control wod token \agent the reading-apparatus of TeX is in state S (skipping blanks) so that the space character following the code-phrase \agent is just dropped and does not yield appending any token to the token stream at all.

In case you need to prevent reducing pairs of hashes to single hashes while appending things, you can combine usage of \edef and usage of \unexpanded:

% Use LaTeX 2e or e-TeX for compiling this example
\def\agent{hash ##: Hello}%
\show\agent
\edef\agent{\unexpanded\expandafter{\agent} \unexpanded{World}}%
\show\agent
\csname stop\endcsname
\bye

Some of the console-output:

> \agent=macro:
->hash ##: Hello.
l.3 \show\agent
               
? 
> \agent=macro:
->hash ##: Hello World.
l.5 \show\agent
               
? 

With your second example, besides confusion about the order in time in which things get expanded, you also have some \csname..\endcsname-expressions with space behind 1 and some \csname..\endcsname-expressions without space behind 1. These yield different control sequence tokens with different names.

% Use LaTeX 2e or e-TeX for compiling this example
\expandafter\def\csname agent1\endcsname{Name of control sequence token has no space behind 1.}%
%
\expandafter\def\csname agent1 \endcsname{hash ##: Hello}%
\expandafter\show\csname agent1 \endcsname
\expandafter\edef\csname agent1 \endcsname{%
  \unexpanded\expandafter\expandafter\expandafter{\csname agent1 \endcsname} \unexpanded{World}%
} 
\expandafter\show\csname agent1 \endcsname
%
\expandafter\show\csname agent1\endcsname
\csname stop\endcsname
\bye

Some of the console-output:

> \agent1 =macro:
->hash ##: Hello.
<recently read> \agent1  
                         
l.5 \expandafter\show\csname agent1 \endcsname
                                              
? 
> \agent1 =macro:
->hash ##: Hello World.
<recently read> \agent1  
                         
l.9 \expandafter\show\csname agent1 \endcsname
                                              
? 
> \agent1=macro:
->Name of control sequence token has no space behind 1..
<recently read> \agent1 
                        
l.11 \expandafter\show\csname agent1\endcsname
                                              
? 

Actually with the examples above you don't need to wrap the phrase "World" into \unexpanded{...} as usually the characters W, o, r, l, d get tokenized as unexpandable explicit character tokens. But in another similar use-case the phrase to append probably does not just consist of characters that get tokenized as unexpandable explicit character tokens but also consists of things that get tokenized as expandable control sequence tokens and/or expandable active character tokens which in turn should not be expanded.

In case you intend to use TeX/LaTeX over a longer period of time get familiar with the terminology and concepts introduced in the TeXbook of Donald E. Knuth. E.g., \expandafter in TeX-jargon is a so-called "primitive" or "primitive control sequence", not an "extension".

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .