因此,我正在寻找一种方法将某些命令设置为几个替代定义之一(即,\mycommand
通过简单的命令调用设置为命令的一个版本或其他版本),并希望能够使用和来做到这一点\let
,\csname
但尝试编译以下内容不会产生任何输出并抱怨missing \begin{document}
,extra \endcsname
和missing \endcsname inserted
。
\documentclass{article}
\newcommand*\letprefix[2]{%
\expandafter\let\csname#1\endcsname\csname#1#2\endcsname %
}
\newcommand*\mycommandversion{I want to use this as ``\\mycommand''}
\letprefix{mycommand}{version}
\begin{document}
Hello!
\mycommand
\end{document}
我也尝试过使用\def
和\newcommand
代替\let
和,在这两种情况下,文档确实被构建但\mycommand
没有打印任何内容(文档只包含“Hello!”),没有任何错误或警告。
我尝试使用 和进行以下操作lualatex
,pdflatex
但没有成功:pdftex
\def\letprefix #1#2{%
\expandafter\let\csname#1\endcsname\csname#1#2\endcsname %
}
\def\mycommandversion {I want to use this as ``\\mycommand''}
\letprefix{mycommand}{version}
Hello!
\mycommand
\bye
我不太熟悉如何使用宏来定义其他宏,我还没有找到任何关于\let
以这种方式使用的信息(也许这是不可能的?),并且根据我对这些的发现,我无法理解为什么带有和的版本\def
不起作用\newcommand
。有没有关于如何实现我想要做的事情以及为什么这段代码不起作用的指示?
答案1
OP 语法的问题在于,第一个\csname
被\let
扩展为一个标记,但第二个没有。一个不错的技巧(从 David C 那里学到的)是,你可以\expandafter
在\csname
之前插入 ,\endcsname
以扩展后面的下一个内容,在结束第一个\csname
扩展之前!这就是我在这里所做的,以便在评估\csname
之前将两个 s 都扩展为 cs-tokens 。\let
\documentclass{article}
\newcommand*\letprefix[2]{%
\expandafter\let\csname#1\expandafter\endcsname\csname#1#2\endcsname}
\newcommand*\mycommandversion{I want to use this as ``\textbackslash
mycommand''}
\letprefix{mycommand}{version}
\begin{document}
Hello!
\mycommand
\end{document}
答案2
您etoolbox
已经有一个方便的工具:
\documentclass{article}
\usepackage{etoolbox}
\newcommand*\letprefix[2]{%
\csletcs{#1}{#1#2}%
}
\newcommand*\mycommandversion{I want to use this as ``\textbackslash mycommand''}
\letprefix{mycommand}{version}
\begin{document}
Hello!
\mycommand
\end{document}
不需要记住复杂的序列\expandafter
。
答案3
答案史蒂文·B·塞格莱特斯说明了一切。
另一种效率较低的方法是申请后交换代币\csname..\endcsname
:
\documentclass{article}
\newcommand\exchange[2]{#2#1}%
\newcommand*\letprefix[2]{%
\expandafter\exchange\expandafter{\csname#1#2\endcsname}{%
\expandafter\let\csname#1\endcsname= %
}%
}%
\newcommand*\mycommandversion{%
I want to use this as ``\textbackslash mycommand''%
}%
\letprefix{mycommand}{version}
\begin{document}
Hello!
\mycommand
\end{document}
(该图片来自 .jpg 文件链接Steven B. Segletes 的回答。)
解决这个问题更“通用”的方法可能是宏观的\NameToCs
:
TeXbook 解释了定义宏时的一个很好的特性:在⟨parameter text⟩
宏定义中,你可以使用#{
-notation 来表示宏,它的最后一个参数将由左花括号分隔{
,与其他参数分隔符不同,它将被重新插入,就像它一直留在原处一样。
因此,我有时会使用一种宏机制\NameToCs
来处理由左花括号()分隔的参数{
和嵌套在花括号中的另一个参数。
嵌套在花括号中的参数表示通过..⟨control sequence token⟩
构造的名称。\csname
\endcsname
\NameToCs
工作原理如下:
\NameToCs⟨stuff not in curly braces⟩{NameOfCs}
→
⟨stuff not in curly braces⟩\NameOfCs
(如果您只希望获取控制序列令牌\NameOfCs
,那么您可以留空⟨stuff not in curly braces⟩
:)\NameToCs{NameOfCs} → \NameOfCs
\makeatletter
\newcommand\exchange[2]{#2#1}%
\@ifdefinable\NameToCs{\long\def\NameToCs#1#{\romannumeral0\innerNameToCs{#1}}}%
\newcommand\innerNameToCs[2]{\expandafter\exchange\expandafter{\csname#2\endcsname}{ #1}}%
\makeatother
此类宏有多种用途:
\NameToCs{foo}
→\foo
\NameToCs\string{foo}
→\string\foo
\NameToCs\meaning{foo}
→\meaning\foo
\NameToCs\global\long\def{foo}...
→\global\long\def\foo...
\NameToCs\newcommand*{foo}...
→\newcommand*\foo...
\NameToCs\NameToCs\global\let{foo}={bar}
→\NameToCs\global\let\foo={bar}
→\global\let\foo=\bar
使用示例 6 可应用于您的场景:
\documentclass{article}
\makeatletter
\newcommand\exchange[2]{#2#1}%
\@ifdefinable\NameToCs{\long\def\NameToCs#1#{\romannumeral0\innerNameToCs{#1}}}%
\newcommand\innerNameToCs[2]{\expandafter\exchange\expandafter{\csname#2\endcsname}{ #1}}%
\makeatother
\newcommand*\mycommandversion{%
I want to use this as ``\textbackslash mycommand''%
}%
\newcommand*\letprefix[2]{%
\NameToCs\NameToCs\let{#1}= {#1#2}%
}%
\letprefix{mycommand}{version}
\begin{document}
Hello!
\mycommand
\end{document}
(该图片来自 .jpg 文件链接Steven B. Segletes 的回答。)
请注意,简单的\let
分配并不总是适用于启动内部由多个宏组成的“基于宏的机制”的控制序列。
- 例如,使用 LaTeX 命令处理根据 定义的可选参数,
\newcommand
或使用由 定义为健壮宏的 LaTeX 命令\DeclareRobustCommand
。使用此类命令,包的
命令\LetLtxMacro
letltxmacro还负责处理内部宏。 xparse
根据's定义的命令也是如此\NewDocumentCommand
。