TeX - LaTeX Asked on October 4, 2021
I have commands called cta
, ctb
, ctc
, ctd
.
I’d like to call these commands from within a loop, as follows:
foreach i in {a,b,c,d}{
% this is where I want to call the command
}
How can I achieve this?
The TeX primitives csname...endcsname
allow to construct macro names.
documentclass{article}
usepackage{tikz}
newcommand*{cta}{A}
newcommand*{ctb}{BB}
newcommand*{ctc}{CCC}
newcommand*{ctd}{DDDD}
begin{document}
foreach i in {a,b,c,d}{The content of texttt{expandafterstringcsname ctiendcsname} is csname ctiendcsname.par}
end{document}
When TeX encounters
csname ctiendcsname
it expands i
(and it keeps expanding) until only character tokens are obtained, and builds a control sequence with the corresponding "string". (An error is thrown if the expansion produces a non-character token.)
Correct answer by campa on October 4, 2021
A fairly general loop:
documentclass{article}
usepackage{xparse}
ExplSyntaxOn
NewDocumentCommand{loopvar}{m +O{} m +O{}}
{
clist_map_inline:nn { #3 } { #2 use:c { #1##1 } #4 }
}
ExplSyntaxOff
newcommand{cta}{texttt{stringcta}}
newcommand{ctb}{texttt{stringctb}}
newcommand{ctc}{texttt{stringctc}}
newcommand{ctd}{texttt{stringctd}}
begin{document}
loopvar{ct}{a,b,c,d}
loopvar{ct}[This is ]{a,b,c}[par]
end{document}
The first mandatory argument is the common part, the second mandatory argument is the list of strings to append; the first optional argument is a “prefix” to add before the macro, the traling one is the “postfix”.
Explanation: use:c
builds a control sequence name from its argument and is essentially csname...endcsname
in disguise; clist_map_inline:nn
maps the comma separated list executing for each item the code specified in the second argument (#1
stands for the current item, but here we need to use ##1
because we're defining a macro).
Answered by egreg on October 4, 2021
As in TeX everything is about tokens that might be expandable, you are faced with the question about the order in time in which expansion of cta
, ctb
, ctc
, ctd
shall take place.
Case 1:
Is the loop to deliver the entire token-sequence ctactbctcctd
?
(In case cta
is a macro that processes three non-delimited arguments, its first argument is the token ctb
, its second argument is the token ctc
and its third argument is the token ctd
.)
Case 2:
Is the token cta
to be produced and expanded totally and carried out completely in the first iteration, then the token ctb
to be produced and expanded totally and carried out completely in the second iteration, then the token ctc
to be produced and expanded totally and carried out completely in the third iteration, then the token ctd
to be produced and expanded totally and carried out completely in the fourth iteration?
How to do the second case has already been shown by campa and egreg.
So let's focus on the first case:
You could use a scratch-macro for accumulating tokens:
documentclass{article}
usepackage{tikz}
newcommand*{cta}[3]{%
noindent
texttt{stringcta}'s first argument is: texttt{string#1}%
texttt{stringcta}'s second argument is: texttt{string#2}%
texttt{stringcta}'s third argument is: texttt{string#3}%
bigskip
#1#2#3%
}
newcommand*{ctb}[2]{%
noindent
texttt{stringctb}'s first argument is: texttt{string#1}%
texttt{stringctb}'s second argument is: texttt{string#2}%
bigskip
#1#2%
}
newcommand*{ctc}[1]{%
noindent
texttt{stringctc}'s first argument is: texttt{string#1}%
bigskip
#1%
}
newcommand*{ctd}{%
noindent
texttt{stringctd} does not process arguments.%
}
newcommandscratchmacro{}%
begin{document}
defscratchmacro{}
foreach i in {a,b,c,d}{%
csname g@addto@macro%
expandafterendcsname
expandafter{%
expandafterscratchmacro
expandafter}%
expandafter{%
csname ctiendcsname
}%
}%
noindent
texttt{stringscratchmacro: meaningscratchmacro}
bigskip
scratchmacro
end{document}
If you wish a fully expandable loop, then you are faced with the task of expandable comma-list-parsing.
In this case you might be interested in the answers to the question newcommand
with many arguments.
If you wish a fully expandable loop, but can make it with a list of non-delimited arguments instead of a comma-list, i.e., something like {a}{b}{c}{d}
instead of a,b,c,d
, then you are faced with the task of expandable argument-list-parsing.
In this case you might be interested in some of the answers to the question defining a new command with variable name in a loop.
Another approach could be:
documentclass{article}
makeatletter
newcommandUD@exchange[2]{#2#1}%
newcommandnameloop{romannumeral0UD@innernameloop}%
newcommandUD@innernameloop[3]{%
ifxrelax#3expandafter@firstoftwoelseexpandafter@secondoftwofi
{ #2}{%
expandafterUD@exchange
expandafter{%
expandafter{%
romannumeral0%
expandafterUD@exchangeexpandafter{%
csname#1#3endcsname
}{ #2}%
}}{%
UD@innernameloop{#1}%
}%
}%
}%
makeatother
expandafterexpandafterexpandafterdef
expandafterexpandafterexpandafterscratchmacro
expandafterexpandafterexpandafter{%
nameloop{ct}{Tokens in front}{a}{b}{c}{d}{relax}%
}%
begin{document}
texttt{stringscratchmacro: meaningscratchmacro}
end{document}
Answered by Ulrich Diez on October 4, 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