csname
我正在尝试定义一个新命令,但使用和“动态”指定新的控制字符串endcsname
。(这是为了实现依赖注入模式。) **或者,有没有办法做到这一点expl3
?
通常,我一直使用\expandafter\newcommand
...但在这个用例中,我想定义csname
WITHIN\newcommand
的第一个参数,如下所示:
\newcommand{\expandbefore\csname \GetCommandName \endcsname}[1]{\small I did it!!!}
\newcommand\expandbefore\csname \GetCommandName \endcsname{\small I did it again!!!}
我希望括号内的所有内容在之前都能够展开\newcommand
-但不依赖\expandafter
之前\newcommand
。
问题:
我很好奇在 LaTeX 中是否有一种正常的方法来做到这一点,而不必依赖另一个进程输入缓冲区 hack(使用 Lua)。
在 的参数中添加
expandafter
、nameuse
、edef
、let
、csname
等\newcommand
只会导致重新定义这些命令的错误。(即使在{}
或begingroup
闭包中。)尝试
\meaning
\expandafter
弄清楚它是如何工作的失败了(可以预见,而且很有趣)。
答案1
我(略作修改)引用我的回答针对这个问题定义控制序列,之后留有空格因为它似乎也适用于你的问题:
通过应用#{
-notation,您可以定义最后一个参数由左括号分隔的宏。与在收集参数时被删除的其他参数分隔符不同,TeX 会保留分隔的左括号。
(实际上,该机制并不局限于左括号字符标记。您可以使用在定义时类别代码为 1 的任何标记。也可以#\WeIrd
在 之后\let\WeIrd={
。)
分隔的参数可以为空。
因此,为了从一组标记中获取控制序列标记,该标记扩展为一组字符标记,该字符标记构成了所讨论的控制序列标记的名称,既可用于定义也可用于调用该控制序列标记,您可以(通过应用 -notation #{
)发明一个控制序列\name
,该控制序列处理一个大括号分隔的参数,后面跟着一个未分隔的参数(嵌套在大括号中)。让 TeX 获取参数后,您可以让 TeX 旋转它们并应用于\csname..\endcsname
大括号内提供的参数。所讨论的控制序列标记的名称也可以包含空格标记。
\makeatletter
%
\newcommand\name{}%
\long\def\name#1#{\UD@innername{#1}}%
%
\newcommand\UD@innername[2]{%
\expandafter\UD@exchange\expandafter{\csname#2\endcsname}{#1}%
}%
%
\newcommand\UD@exchange[2]{#2#1}%
%
\makeatother
\name foo{bar}
→ 扩展步骤 1:
\UD@innername{foo}{bar}
→ 扩展步骤 2:
\expandafter\UD@exchange\expandafter{\csname bar\endcsname}{foo}
→ 扩展步骤 3:
\UD@exchange{\bar}{foo}
→ 扩展步骤 4
foo\bar
:。
在扩展上下文中,您需要四个\expandafter
链才能获得结果。
由于\romannumeral
遇到非正数时不会产生任何标记,因此您可以添加一些\romannumeral
-expansion以减少\expandafter
-chains的数量。
要么这样做\romannumeral\name0 foo{bar}
。这样,只需要一个命中令牌\expandafter
的链即可。\romannumeral
或者将\romannumeral
-expansion “硬编码”在定义中 — 这样就\expandafter
需要两个 -chains。第一个用于获取 的顶层扩展\name
。第二个用于诱导\romannumeral
-expansion。
\makeatletter
%
\newcommand\name{}%
\long\def\name#1#{\romannumeral0\UD@innername{#1}}%
%
\newcommand\UD@innername[2]{%
\expandafter\UD@exchange\expandafter{\csname#2\endcsname}{ #1}%
}%
%
\newcommand\UD@exchange[2]{#2#1}%
%
\makeatother
使用这样的宏,您就不会受到特定定义命令的约束:
\name{foo}
→ \foo
。 (←这是您不定义而只是通过调用/使用控制序列的方式\name
。)
\name\newcommand{foo}
→ \newcommand\foo
。
\name\DeclareRobustCommand{foo}
→ \DeclareRobustCommand\foo
。
\name\global\long\outer\def{foo}
→ \global\long\outer\def\foo
。
\name\expandafter{foo}\bar
→ \expandafter\foo\bar
。
\name\let{foo}=\bar
→ \let\foo=\bar
。
\name\string{foo}
→ \string\foo
。
\name\meaning{foo}
→ \meaning\foo
。
您也可以使用这样的宏来定义/调用名称包含空格的宏:
\name{foo }
→ \foo␣
。
\name\newcommand{foo }
→ \newcommand\foo␣
。
\name\DeclareRobustCommand{foo }
→ \DeclareRobustCommand\foo␣
。
\name\global\long\outer\def{foo }
→ \global\long\outer\def\foo␣
。
\name\expandafter{foo }\bar
→ \expandafter\foo␣\bar
。
\name\let{foo }=\bar
→ \let\foo␣=\bar
。
\name\string{foo }
→ \string\foo␣
。
\name\meaning{foo }
→ \meaning\foo␣
。
在收集有问题的控制序列标记的名称时,\name
将触发可扩展标记的扩展:
\def\GetCommandName{FooBar}
\name\newcommand{\GetCommandName}[1]{\small I did it!!!}
→\newcommand\FooBar[1]{\small I did it!!!}
\def\GetCommandName{\CommandNamePartA\CommandNamePartB}
\def\CommandNamePartA{Ba}
\def\CommandNamePartB{r\InnerCommandNamePart o}
\def\InnerCommandNamePart{Fo}
\name\newcommand{\GetCommandName}{\small I did it again!!!}
→\newcommand\BarFoo{\small I did it again!!!}
您还可以嵌套调用\name
:
示例 1:
\name\name\expandafter{f o o }{b a r }
处理第一个\name
收益:
\name\expandafter\f␣o␣o␣{b a r }
。
处理第二个\name
收益:
\expandafter\f␣o␣o␣\b␣a␣r␣
。
(类似地:\name\name\let{f o o }={b a r }
→ \let\f␣o␣o␣=\b␣a␣r␣
。)
示例 2:
\name\name\name\expandafter\expandafter\expandafter{f o o }\expandafter{b a r }{c r a z y }
处理第一个\name
收益:
\name\name\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter{b a r }{c r a z y }
。
处理第二个\name
收益:
\name\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣{c r a z y }
。
处理第三个\name
得到:
\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣\c␣r␣a␣z␣y␣
。
示例 3:
在扩展上下文中,您可以使用\romannumeral
-expansion 来使事情继续进行。
\romannumeral\name\name\name0 \expandafter\expandafter\expandafter{f o o }\expandafter{b a r }{c r a z y }
\romannumeral
不断扩展,直到找到某个数字。最后它会找到数字0
,而对于非正数\romannumeral
,不会传递任何 token:
%\romannumneral-expansion in progress
\name\name\name0 \expandafter\expandafter\expandafter{f o o }\expandafter{b a r }{c r a z y }
处理第一个\name
收益:
%\romannumneral-expansion in progress
\name\name0 \expandafter\expandafter\expandafter\f␣o␣o␣\expandafter{b a r }{c r a z y }
。
处理第二个\name
收益:
%\romannumneral-expansion in progress
\name0 \expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣{c r a z y }
。
处理第三个\name
得到:
%\romannumneral-expansion in progress
0 \expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣\c␣r␣a␣z␣y␣
。
现在\romannumeral
找到数字0
。因此\romannumeral
-expansion 被中止并且\romannumeral
不会传递任何令牌:
\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣\c␣r␣a␣z␣y␣
。
请注意,\name
内部\csname
适用
可扩展标记的扩展发生在
\csname
搜索匹配期间,\endcsname
收集形成相关控制序列标记名称的字符标记。\csname
作为副作用,应用会产生将\relax
-primitive 的含义分配给相关控制序列的效果,以防相关控制序列在应用之前未定义。即使在应用时 -parameter 具有正值,\csname
该分配也将限制在当前范围内。\globaldefs
\csname
%%\errorcontextlines=1000
\documentclass[a4paper]{article}
\usepackage{textcomp}%
\parindent=0cm
\parskip=\medskipamount
\makeatletter
\newcommand\name{}%
\long\def\name#1#{\romannumeral0\UD@innername{#1}}%
\newcommand\UD@innername[2]{%
\expandafter\UD@exchange\expandafter{\csname#2\endcsname}{ #1}%
}%
\newcommand\UD@exchange[2]{#2#1}%
\makeatother
\name\newcommand{foo}[2]{%
Control sequence whose name does not contain any space.\\
Argument 1: \textit{\textlangle#1\textrangle}\\
Argument 2: \textit{\textlangle#2\textrangle}
}%
\name\newcommand{foo }[2]{%
Control sequence whose name has a trailing space.\\
Argument 1: \textit{\textlangle#1\textrangle}\\
Argument 2: \textit{\textlangle#2\textrangle}
}%
\name\newcommand{ f o o }[2]{%
Control sequence whose name is interspersed with spaces.\\
Argument 1: \textit{\textlangle#1\textrangle}\\
Argument 2: \textit{\textlangle#2\textrangle}
}%
\newcommand*\GetCommandName{\CommandNamePartA\CommandNamePartB}
\newcommand*\CommandNamePartA{Ba}
\newcommand*\CommandNamePartB{r\InnerCommandNamePart o}
\newcommand*\InnerCommandNamePart{Fo}
\name\newcommand{\GetCommandName}{\small I did it again!!!}
\begin{document}
\name{foo}{Arg 1}{Arg 2}
\name{foo }{Arg 1}{Arg 2}
\name{ f o o }{Arg 1}{Arg 2}
Nesting \texttt{\string\name}:
\name\expandafter\newcommand\expandafter*\expandafter{C o N f u SiO n}\expandafter{%
\romannumeral\name\name\name0 %
\expandafter\expandafter\expandafter{F O O}\expandafter{B A R}{C R A Z Y}%
}%
\texttt{\name\string{C o N f u SiO n} is \name\meaning{C o N f u SiO n}}%
\\
Playing around with expandable tokens:
\texttt{\name\string{\GetCommandName}:}
\texttt{\name\meaning{\GetCommandName}}
\name{\GetCommandName}%
Playing around with grouping:
%Be aware that \texttt itself opens up a new scope for typesetting its argument.
%\globaldefs=1\relax
\texttt{%
\begingroup\name\string{w e i r d } is \name\endgroup\meaning{w e i r d }%
}%
\texttt{%
\name\string{w e i r d } is \name\meaning{w e i r d }%
}%
\end{document}
答案2
LaTeX 已经有一个命令形式,它采用命令名称而不是 csname 标记:
\@namedef{\GetCommandName}{\small I did it!!!}
应该做你想做的事,这只是\expandafter\def\csname\GetCommandName\endcsname{..}