TeX - LaTeX Asked by Patrick Häcker on March 18, 2021
I seem to lack understanding the combination of an edef
and the definition of a macro inside another definition. This is probably best shown with an example.
The following MWE (I know it is not really useful so minimal) does not work:
documentclass{article}
defmymacro#1{%
defdo##1{##1}%
#1%
}
begin{document}
edefsavedValue{mymacro{argument}}%
savedValue: savedValue
end{document}
The error is in the edef
line and states Illegal parameter number in definition of savedValue
.
I thought that the edef
would expand mymacro{argument}
, which first expands defdo##1{##1}
to nothing leading to the output argument
, which is then saved into savedValue
. Without the line defdo##1{##1}
this seems to work as explained, but with that line I get the mentioned error.
What is the problem here?
Using xparse
‘s command does work
documentclass{article}
usepackage{xparse}
NewDocumentCommand{mymacro}{m}{%
defdo##1{##1}%
#1%
}
begin{document}
edefsavedValue{mymacro{argument}}%
savedValue: savedValue
end{document}
and gives the expected output:
What is the actual difference to using def
?
the def
operation is not expandable. In an edef
def
is simply a non-expandable token that stays unchanged. Similarly mymacro
ends up being defined by protecteddef
so does not expand in an edef
.
showmymacro
shows
> mymacro=protected macro:
#1->def do ##1{##1}#1.
documentclass{article}
usepackage{xparse}
NewDocumentCommand{mymacro}{m}{%
defdo##1{##1}%
#1%
}
begin{document}
edefsavedValue{mymacro{argument}}%
savedValue: savedValue
end{document}
So there are no expandable tokens in the definition of savedValue
so the edef
is equivalent to def
in this case. showsavedValue
shows
> savedValue=macro:
->mymacro {argument}.
Then when savedValue
is expanded it is equivalent to
defdo#1{#1}argument
If you use def
rather than protecteddef
then when you get to the edef
mymacro
expands (but def
doesn't so it is the same as
edefsavedValue{mymacro{argument}}%
is
edefsavedValue{defdo##1{##1}argument}%
then it tries to expand def
but that is not expandable so it is left, then it tries to expand do
so you get the expansion of whatever random definition do
has at this point which happens to be
do=noexpand.
and things go wrong....
Correct answer by David Carlisle on March 18, 2021
Another possibility to avoid the problems explained by David Carlisle is to completely avoid the edef
. The trick is to put the saving inside of the "called function". This is a bit similar to C, where a pointer to the allocated memory is given as the first argument to a function and is normally an input-output-parameter. This pattern can probably save a lot of trouble with LaTeX.
documentclass{article}
usepackage{etoolbox}
defmymacro#1#2{%
defdo##1{##1}%
csdef{#1}{#2}%
}
begin{document}
mymacro{savedValue}{argument}%
savedValue: savedValue
end{document}
Answered by Patrick Häcker on March 18, 2021
Your code is:
documentclass{article}
defmymacro#1{%
defdo##1{##1}%
#1%
}
begin{document}
edefsavedValue{mymacro{argument}}%
savedValue: savedValue
end{document}
Two facts are relevant for the explanation:
When a macro is expanded whose ⟨definition text⟩ contains sequences of hashes, then the amount of hashes in these sequences will be halved in the result of expanding the macro.
With the code of your example mymacro{⟨argument⟩}
yields defdo#1{#1}⟨argument⟩
. The amount of hashes in all sequences ##
of the ⟨definition text⟩ of mymacro
is halved to #
in the result of expanding mymacro
.
def
is not expandable, therefore will not be removed from the token stream but will be left untouched during edef
-expansion. (Unless an expandable control sequence is expanded during edef
-expansion whose expansion yields removal of the def
-token as is the case with gobble
in the following: defgobble#1{} ... edeffoo{gobbledef whatsoever}
.)
With edefsavedValue{mymacro{argument}}
expandable tokens of the sequence mymacro{argument}
are expanded until no expandable tokens are left before the assignment takes place.
def
is not expandable. Therefore just expanding all expandable tokens in/coming from mymacro{argument}
yields:
def⟨total expansion of all expandable tokens in the sequence "do#1{#1}argument" ⟩
If do
is defined as something unexpandable, then just expanding all expandable tokens in/coming from mymacro{argument}
yields the token-sequence:
defdo#1{#1}argument
.
The likelihood is very high that the token-sequence that comes into being during edef
-expansion as the expanded ⟨definition text⟩ contains #1
although the ⟨parameter text⟩ of the edef
-definition of savedValue
is empty/does not introduce an argument #1
.
That's why you get the error Illegal parameter number in definition of savedValue.
In case do
is undefined, you will get the error
! Undefined control sequence.
mymacro #1->def do
prior to the error Illegal parameter number in definition of savedValue.
The following example might provide better understanding of how edef
works:
documentclass{article}
defmymacro#1{%
defdo{#1}%
}
begin{document}
letdo=relax
edefmacro{mymacro{bar}}
showmacro
letfoo=relax
defdo{foo}%
edefmacro{mymacro{bar}}
showmacro
deffoo{hoo}%
lethoo=relax
edefmacro{mymacro{bar}}
showmacro
end{document}
Console output:
[...]
> macro=macro:
->def do {bar}.
l.8 showmacro
?
> macro=macro:
->def foo {bar}.
l.12 showmacro
?
> macro=macro:
->def hoo {bar}.
l.16 showmacro
?
[...]
The following example might provide better understanding of the treatment of sequences of hashes during expansion:
documentclass{article}
makeatletter
newcommandstringifytillrelax[1]{%
ifxrelax#1expandafter@firstoftwoelseexpandafter@secondoftwofi
{}{string#1stringifytillrelax}
}%
makeatother
begin{document}
defmymacro{##}%
message{^^JSee the amount of hashes that comes from expanding stringmymacro: expandafterstringifytillrelaxmymacrorelax}%
defmymacro{####}%
message{^^JSee the amount of hashes that comes from expanding stringmymacro: expandafterstringifytillrelaxmymacrorelax}%
defmymacro{######}%
message{^^JSee the amount of hashes that comes from expanding stringmymacro: expandafterstringifytillrelaxmymacrorelax}%
defmymacro{########}%
message{^^JSee the amount of hashes that comes from expanding stringmymacro: expandafterstringifytillrelaxmymacrorelax}%
edefmymacrob{mymacro}
message{^^JSee the amount of hashes that comes from expanding stringmymacrob: expandafterstringifytillrelaxmymacrobrelax}%
edefmymacroc{mymacrob}
message{^^JSee the amount of hashes that comes from expanding stringmymacroc: expandafterstringifytillrelaxmymacrocrelax}%
end{document}
Console output:
[...]
See the amount of hashes that comes from expanding mymacro: #
See the amount of hashes that comes from expanding mymacro: ##
See the amount of hashes that comes from expanding mymacro: ###
See the amount of hashes that comes from expanding mymacro: ####
See the amount of hashes that comes from expanding mymacrob: ##
See the amount of hashes that comes from expanding mymacroc: #
[...]
Answered by Ulrich Diez on March 18, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP