1

I have a problem with expanding and not expanding macros together with \csname. Please look at the following code:

% Save content of a macro via \csname
\def\LetCsVar#1#2{\expandafter\let\csname#1\endcsname=#2}% #1=name, #2=definition
% Get the previous saved content of a macro via \csname
\def\GetCsVar#1{\csname#1\endcsname}% #1=name

\def\txt{Hallo\thinspace you}
txt: \meaning\txt\break  % prints: txt: macro:->Hallo\thinspace you

\count1=1
\LetCsVar{test\the\count1}{\txt}

% \txt can change here
\def\txt{Holla}

\def\a{\GetCsVar{test\the\count1}}
a: \meaning\a\break      % prints: a: macro:->\GetCsVar {test\the \count 1}

\edef\b{\GetCsVar{test\the\count1}}
b: \meaning\b\break      % prints: b: macro:->Hallo\kern .16667em you

\toks0=\expandafter{\a}
\edef\c{\the\toks0}
c: \meaning\c\break      % prints: c: macro:->\GetCsVar {test\the \count 1}

I wonder what to do to get a new macro after \GetCsVar with the meaning macro:->Hallo\thinspace you like the \txt-macro.

The problem is to expand \GetCsVar and \the \count 1 and \csname but not the initial \thinspace.

Thank you, Peter

Edit:

Inspired by David Carlisles answer i created a new macro \GetCsVarEx:

\def\GetCsVarExp#1#2%
{%
\expandafter\expandafter\expandafter%
\def%
\expandafter\expandafter\expandafter%
\ax%
\expandafter\expandafter\expandafter%
{\csname#1\endcsname}%
\expandafter\let\csname#2\endcsname=\ax
}

Then i call

\GetCsVarExp{test\the\count1}{d}

and so d: \meaning\d prints my desired d: macro:->Hallo\thinspace you.

It works, but i would appreciate an even simpler way to fill the macro \d. Perhaps there is no?

5
  • it doesn't really help to edit in answers to the question after answers are posted, it makes the question and answer format of the site hard to follow for later readers. Mar 8 at 13:52
  • you are using plain tex you could still use expl3, for layex users where expl3 is built-in, all this csname and expandafter control is handled with pre-defined functions. so you never have to do this by hand. Mar 8 at 13:53
  • 1
    why \def \ax and then do \let rather than simply defining \csname#2\endcsname directly? Mar 8 at 13:55
  • because i couldn't find out how to set all the expandafters than.
    – coonlight
    Mar 8 at 15:03
  • Don't edit in answers to questions. However, there is nothing wrong with questioners writing separate answers to their own questions themselves. Mar 9 at 10:47

4 Answers 4

1

The tokcycle package, while intended for other purposes, has the expansion infrastructure built into it. It will allow a simpler definition of \GetCsVarExp plus allow specified levels of expansion.

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{tokcycle}
\newcommand\GetCsVarExp[3][]{\cytoks{}%
  \addcytoks[#1]{\csname#2\endcsname}%
  \expandafter\def\csname#3\expandafter\endcsname
    \expandafter{\the\cytoks}}
\begin{document}
% Save content of a macro via \csname
\def\LesVar#1#2{\expandafter\let\csname#1\endcsname=#2}% #1=name, #2=definition
% Get the previous saved content of a macro via \csname
\def\GesVar#1{\csname#1\endcsname}% #1=name

\def\txt{Hallo\thinspace you}
txt: \meaning\txt  % prints: txt: macro:->Hallo\thinspace you

\count1=1
\LesVar{test\the\count1}{\txt}

% \txt can change here
\def\txt{Holla}

\GetCsVarExp{test\the\count1}{A}% 0 EXPANSIONS
A: \meaning\A

\GetCsVarExp[1]{test\the\count1}{B}% 1 EXPANSION
B: \meaning\B

\GetCsVarExp[2]{test\the\count1}{C}% 2 EXPANSIONS
C: \meaning\C

\GetCsVarExp[x]{test\the\count1}{D}% FULLY EXPANDED
D: \meaning\D
\end{document}

enter image description here

If the result is required in PlainTex, that is no problem, as tokcycle is available there, as well:

\input tokcycle.tex
\long\def\GetCsVarExp[#1]#2#3{\cytoks{}%
  \addcytoks[#1]{\csname#2\endcsname}%
  \expandafter\def\csname#3\expandafter\endcsname
    \expandafter{\the\cytoks}}

% Save content of a macro via \csname
\def\LesVar#1#2{\expandafter\let\csname#1\endcsname=#2}% #1=name, #2=definition
% Get the previous saved content of a macro via \csname
\def\GesVar#1{\csname#1\endcsname}% #1=name

\def\txt{Hallo\thinspace you}
txt: {\tt\meaning\txt}  % prints: txt: macro:->Hallo\thinspace you

\count1=1
\LesVar{test\the\count1}{\txt}

% \txt can change here
\def\txt{Holla}

\GetCsVarExp[0]{test\the\count1}{A}% 0 EXPANSIONS
A: {\tt\meaning\A}

\GetCsVarExp[1]{test\the\count1}{B}% 1 EXPANSION
B: {\tt\meaning\B}

\GetCsVarExp[2]{test\the\count1}{C}% 2 EXPANSIONS
C: {\tt\meaning\C}

\GetCsVarExp[x]{test\the\count1}{D}% FULLY EXPANDED
D: {\tt\meaning\D}

\bye
2
1

enter image description here

You want to expand three times, so need seven\expandafter

% Save content of a macro via \csname
\def\LetCsVar#1#2{\expandafter\let\csname#1\endcsname=#2}% #1=name, #2=definition
% Get the previous saved content of a macro via \csname
\def\GetCsVar#1{\csname#1\endcsname}% #1=name

\def\txt{Hallo\thinspace you}
txt: {\tt\meaning\txt}

\count2=1 %even registers for local assignments
\LetCsVar{test\the\count2}{\txt}

% \txt can change here
\def\txt{Holla}
txt: {\tt\meaning\txt}

\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter
\expandafter
\def
\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter
\expandafter
\b
\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter
\expandafter{\GetCsVar{test\the\count2}}

{\tt\meaning\b}

or as a macro

\def\foo#1#2{%
\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter
\expandafter
\def
\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter
\expandafter
#1%
\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter
\expandafter{\GetCsVar{#2}}
}

\foo\abc{test\the\count2}

{\tt\meaning\abc}
\bye

\foo above is the same as the later requested GetCsVarExp apart from the argument order so you could instead use:



\def\GetCsVarExp#1#2%
{%
\expandafter
\def
\csname#2\expandafter\expandafter\expandafter\endcsname
\expandafter\expandafter\expandafter
{\csname#1\endcsname}%
}


\GetCsVarExp{test\the\count2}{d}
  
{\tt\meaning\d}
5
  • Wow! That are many expandafters :-) Thank you for this, it works! But its so much code i have to type every time when i want to use \GetCsVar. I do not find a way to put this in a macro for a more easy call.
    – coonlight
    Mar 8 at 12:08
  • I put an easier way of this in my Edit above, but i am searching for an even more easy way :-)
    – coonlight
    Mar 8 at 13:19
  • Your definition of \foo doesn't work for me. I had to move the first { right before the first \expandafter, but than the result is not the right one, but something like macro: \expandafter \expandafter \expandafter \expandafter \expandafter \expandafter \expandafter \GetCsVar ->test\the \count 1
    – coonlight
    Mar 8 at 15:01
  • @coonlight I added a \GetCsVarExp version without the \let to a temporary name Mar 8 at 17:17
  • Great, David. The new \GetCsVarExp works fine! :-) Thank you very much for your support!
    – coonlight
    Mar 8 at 18:56
1

I'm not sure why you want to go into such complications. If you want an infrastructure to do repeatedly things like these, it's quite easy.

\def\LetCsVar#1#2{% #1=name, #2=definition
  \expandafter\let\csname#1\endcsname=#2%
}
\def\LetCsVarCs#1#2{% #1=name, #2=definition
  \expandafter\let\csname#1\expandafter\endcsname\csname#2\endcsname
}
\def\DefVarCs#1#2{% #1=name, #2=definition
  \expandafter\let\expandafter#1\csname#2\endcsname
}

\tt\frenchspacing

\def\txt{Hallo\thinspace you}
txt: \meaning\txt

\count255=1
\LetCsVar{test\the\count255}{\txt}

% \txt can change here
\def\txt{Holla}

\LetCsVarCs{a}{test\the\count255}
a: \meaning\a

\DefVarCs\b{test\the\count255}
b: \meaning\b

\bye

Note that \LetCsVarCs does the same as your \GetCsVarExp (only with reversed order of arguments).

enter image description here

By the way, \break is completely out of place where you use it.

1

I wonder what to do to get a new macro after \GetCsVar with the meaning macro:->Hallo\thinspace you like the \txt-macro.

The problem is to expand \GetCsVar and \the \count 1 and \csname but not the initial \thinspace.

If you really want to do it via expansion, be aware that three single expansion steps need to be triggered which requires inserting sequences of 2k-1; k=3 tokens \expandafter = 7 tokens \expandafter.

The first expansion step delivers the replacement text of \GetCsVar with parameters replaced by the arguments grabbed from the token stream. That replacement text is a \csname..\endcsname-thingie.

The second expansion step turns the \csname..\endcsname-thingie into the corresponding control sequence token. In case that control sequence token is not already having a meaning/is undefined in the current scope, then it (only in the current scope, even if \globaldefs has a positive value) also is given the meaning of the primitive \relax.

The third expansion step - in case it is expandable - delivers "toplevel-expansion" of that control sequence token.


But I suggest avoiding expanding the macro whose definition/meaning is to be "copied":

  • In the edge case of the ⟨replacement text⟩ of the definition of the macro to be copied containing several consecutive hashes, the ⟨replacement text⟩ of the definition of the macro which holds the copy might be different from the ⟨replacement text⟩ of the definition of the macro which holds the original. This is because two consecutive hashes ## in the ⟨replacement text⟩ of the definition of a macro are collapsed into a single hash # at the time of obtaining the replacement text due to expanding that macro.

  • You loose information about definition-prefixes like \long (or \protected when using a TeX-engine with builtin expansion-protection.)

You might not have heard about the \expandafter\endcsname\csname-trick yet...

In order to avoid the need of expanding macros and hereby loosing information about definition-prefixes and hereby facing the circumstance that with each expansion amounts of consecutive hashes occurring in the replacement text are halved I suggest using only \let, no intermediate scratch-macro-assignment.

\tt

%----------------------------------------------------------------------
\def\GetCsVarExp#1#2{%
  \expandafter\let\csname#2\expandafter\endcsname\csname#1\endcsname
}%
%----------------------------------------------------------------------
% Save content of a macro via \csname
\def\LetCsVar#1#2{\expandafter\let\csname#1\endcsname=#2}% #1=name, #2=definition
% Get the previous saved content of a macro via \csname
\def\GetCsVar#1{\csname#1\endcsname}% #1=name

\def\txt{Hello\thinspace you}%
txt: \meaning\txt % prints: txt: macro:->Hallo\thinspace you

\count1=1
\LetCsVar{test\the\count1}{\txt}%

% \txt can change here
\def\txt{Holla}%

\GetCsVarExp{test\the\count1}{d}%

d: \meaning\d      % prints: d: macro:->Hallo\thinspace you.

\bye

Let's exhibit the difference by defining a macro which expands to a bunch of hashes and seeing what happens to the definitions when "copying":

%----------------------------------------------------------------------

\tt

\def\GetCsVarExpcoonlight#1#2{%
  \expandafter\expandafter\expandafter\def
  \expandafter\expandafter\expandafter\ax
  \expandafter\expandafter\expandafter{%
    \csname#1\endcsname
  }%
  \expandafter\let\csname#2\endcsname=\ax
}%
%----------------------------------------------------------------------
\def\GetCsVarExpUlrich#1#2{%
  \expandafter\let\csname#2\expandafter\endcsname\csname#1\endcsname
}%
%----------------------------------------------------------------------
% Save content of a macro via \csname
\def\LetCsVar#1#2{\expandafter\let\csname#1\endcsname=#2}% #1=name, #2=definition
% Get the previous saved content of a macro via \csname
\def\GetCsVar#1{\csname#1\endcsname}% #1=name

\def\SomeHashes{Initially there are 16 hashes: ################}%

%----------------------------------------------------------------------

\LetCsVar{SomeHashes0coonlight}{\SomeHashes}%
\expandafter\string\csname SomeHashes0coonlight\endcsname:\hfil\break
\expandafter\meaning\csname SomeHashes0coonlight\endcsname 

%----------------------------------------------------------------------

\GetCsVarExpcoonlight{SomeHashes0coonlight}{SomeHashes1coonlight}%
\expandafter\string\csname SomeHashes1coonlight\endcsname:\hfil\break
\expandafter\meaning\csname SomeHashes1coonlight\endcsname 

\GetCsVarExpcoonlight{SomeHashes1coonlight}{SomeHashes2coonlight}%
\expandafter\string\csname SomeHashes2coonlight\endcsname:\hfil\break
\expandafter\meaning\csname SomeHashes2coonlight\endcsname 

\GetCsVarExpcoonlight{SomeHashes2coonlight}{SomeHashes3coonlight}%
\expandafter\string\csname SomeHashes3coonlight\endcsname:\hfil\break
\expandafter\meaning\csname SomeHashes3coonlight\endcsname 

% This would yield an error as the single hash is taken 
% for s.th. that denotes a parameter in wrong ways (single
% hash not trailed by single digit in range 1..9; no
% parameters declared in the definition's parameter text, thus
% parameters #1 .. #9 may not occur in the replacement text) :

%\GetCsVarExpcoonlight{SomeHashes3coonlight}{SomeHashes4coonlight}%
%\expandafter\string\csname SomeHashes4coonlight\endcsname:\hfil\break
%\expandafter\meaning\csname SomeHashes4coonlight\endcsname 


%----------------------------------------------------------------------

\LetCsVar{SomeHashes0Ulrich}{\SomeHashes}%
\expandafter\string\csname SomeHashes0Ulrich\endcsname:\hfil\break
\expandafter\meaning\csname SomeHashes0Ulrich\endcsname 

%----------------------------------------------------------------------

\GetCsVarExpUlrich{SomeHashes0Ulrich}{SomeHashes1Ulrich}%
\expandafter\string\csname SomeHashes1Ulrich\endcsname:\hfil\break
\expandafter\meaning\csname SomeHashes1Ulrich\endcsname 

\GetCsVarExpUlrich{SomeHashes1Ulrich}{SomeHashes2Ulrich}%
\expandafter\string\csname SomeHashes2Ulrich\endcsname:\hfil\break
\expandafter\meaning\csname SomeHashes2Ulrich\endcsname 

\GetCsVarExpUlrich{SomeHashes2Ulrich}{SomeHashes3Ulrich}%
\expandafter\string\csname SomeHashes3Ulrich\endcsname:\hfil\break
\expandafter\meaning\csname SomeHashes3Ulrich\endcsname 

% This does not yield an error:

\GetCsVarExpUlrich{SomeHashes3Ulrich}{SomeHashes4Ulrich}%
\expandafter\string\csname SomeHashes4Ulrich\endcsname:\hfil\break
\expandafter\meaning\csname SomeHashes4Ulrich\endcsname 

%----------------------------------------------------------------------

\bye

enter image description here


In case you are interested:

As described in answers to other questions, using #{-notation you can implement a macro \CsNameToCsToken with the following syntax:

\CsNameToCsToken⟨stuff not in braces⟩{⟨NameOfCs⟩}

⟨stuff not in braces⟩\NameOfCs

(⟨stuff not in braces⟩ may be empty.)

Definition in plain-TeX:

\chardef\stopromannumeral=`\^^00 %
\long\def\CsNameToCsToken#1#{\romannumeral\InnerCsNameToCsToken{#1}}%
\long\def\InnerCsNameToCsToken#1#2{%
  \expandafter\exchange\expandafter{\csname#2\endcsname}{\stopromannumeral#1}%
}%
\long\def\exchange#1#2{#2#1}%

(Due to \romannumeral-expansion the result is obtained by triggering two expansion-steps, e.g., by having two "hits" with \expandafter which requires sequences of 2k-1; k=2 tokens \expandafter= sequences of 3 tokens \expandafter.)

With such a macro you are not bound to specific definition commands:

\CsNameToCsToken{foo}\foo  .

\CsNameToCsToken\newcommand{foo}\newcommand\foo  .

\CsNameToCsToken\DeclareRobustCommand{foo}\DeclareRobustCommand\foo  .

\CsNameToCsToken\NewDocumentCommand{foo}\NewDocumentCommand\foo  .

\CsNameToCsToken\global\long\outer\def{foo}\global\long\outer\def\foo  .

\CsNameToCsToken\expandafter{foo}\bar\expandafter\foo\bar  .

\CsNameToCsToken\expandafter\foo{bar}\expandafter\foo\bar  .

\CsNameToCsToken\CsNameToCsToken\expandafter{foo}{bar}\CsNameToCsToken\expandafter\foo{bar}\expandafter\foo\bar  .

\CsNameToCsToken\string{foo}\string\foo  .

\CsNameToCsToken\meaning{foo}\meaning\foo  .

\CsNameToCsToken\let{foo}=\bar\let\foo=\bar  .

\CsNameToCsToken\let\foo={bar}\let\foo=\bar  .

\CsNameToCsToken\CsNameToCsToken\global\let{foo=}{bar}\CsNameToCsToken\global\let\foo={bar}\global\let\foo=\bar  .

With \long\def\PassFirstToSecond#1#2{#2{#1}} and \def\bar{AAA}:
\CsNameToCsToken\PassFirstToSecond{bar}{\CsNameToCsToken\expandafter\def\expandafter{foo}\expandafter}
\PassFirstToSecond\bar{\CsNameToCsToken\expandafter\def\expandafter{foo}\expandafter}
\CsNameToCsToken\expandafter\def\expandafter{foo}\expandafter{\bar}
\expandafter\def\expandafter\foo\expandafter{\bar}
\def\foo{AAA}  .


In your scenario you could do:

\tt
%------------------------------------------------------------------------------
\chardef\stopromannumeral=`\^^00 %
\long\def\CsNameToCsToken#1#{\romannumeral\InnerCsNameToCsToken{#1}}%
\long\def\InnerCsNameToCsToken#1#2{%
  \expandafter\exchange\expandafter{\csname#2\endcsname}{\stopromannumeral#1}%
}%
\long\def\exchange#1#2{#2#1}%
%------------------------------------------------------------------------------

\def\txt{Hallo\thinspace you}
txt: \meaning\txt  % prints: txt: macro:->Hallo\thinspace you

\CsNameToCsToken\let{test1}=\txt
test1: \CsNameToCsToken\meaning{test1}% prints: test1: macro:->Hallo\thinspace you

\CsNameToCsToken\CsNameToCsToken\let{test2}={test1}%
test2: \CsNameToCsToken\meaning{test2}% prints: test2: macro:->Hallo\thinspace you

\CsNameToCsToken\let\teeext={test2}%
teeext: \meaning\teeext % prints: teeext: macro:->Hallo\thinspace you

\bye

enter image description here


In a comment you say you couldn't find out how to set all the \expandafter.

You might be interested in the answers to How can I know the number of expandafters when appending to a csname macro?.

You must log in to answer this question.

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