在宏中使用 \let 和 \csname

在宏中使用 \let 和 \csname

因此,我正在寻找一种方法将某些命令设置为几个替代定义之一(即,\mycommand通过简单的命令调用设置为命令的一个版本或其他版本),并希望能够使用和来做到这一点\let\csname但尝试编译以下内容不会产生任何输出并抱怨missing \begin{document}extra \endcsnamemissing \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!”),没有任何错误或警告。

我尝试使用 和进行以下操作lualatexpdflatex但没有成功: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}

这是 Steven B. Segletes 答案的 .jpg 文件的链接。
(该图片来自 .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

此类宏有多种用途:

  1. \NameToCs{foo}\foo
  2. \NameToCs\string{foo}\string\foo
  3. \NameToCs\meaning{foo}\meaning\foo
  4. \NameToCs\global\long\def{foo}...\global\long\def\foo...
  5. \NameToCs\newcommand*{foo}...\newcommand*\foo...
  6. \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}

这是 Steven B. Segletes 答案的 .jpg 文件的链接。
(该图片来自 .jpg 文件链接Steven B. Segletes 的回答。


请注意,简单的\let分配并不总是适用于启动内部由多个宏组成的“基于宏的机制”的控制序列。

  • 例如,使用 LaTeX 命令处理根据 定义的可选参数,\newcommand或使用由 定义为健壮宏的 LaTeX 命令\DeclareRobustCommand。使用此类命令,包的
    命令\LetLtxMacroletltxmacro还负责处理内部宏。
  • xparse根据's定义的命令也是如此\NewDocumentCommand

相关内容