请参阅下面的定义机制,其中我根据另外两个宏定义来定义一个宏。
\documentclass{article}
\begin{document}
\def\a{A}
\def\b{B}
\def\c{CC}
\def\expandafter\csname\a\b\endcsname{\c}
\AB
\end{document}
我定义它的方式是,我认为\a
和\b
正在构建
AB
。因此,\AB
应该扩展到CC
。
但执行时,我收到消息,
! Undefined control sequence. l.12 \AB ?
该文件无需\AB
调用即可顺利编译。因此,某种定义机制正在发生。
我的错误在哪里?
答案1
使用您当前的代码
\def\expandafter\csname\a\b\endcsname{\c}
你正在覆盖TeX
原语\expandafter
,这样它必须随后是\csname\a\b\endcsname
。然后构造
\expandafter\csname\a\b\endcsname
将扩展为\c
。对于空文档,什么也不会发生,但是尝试输入一些内容,你就会得到一个令人费解的
! Use of \expandafter doesn't match its definition.
你做想要的是坚持\def
一段时间,所以你必须把\expandafter
前它。代码
\documentclass{article}
\begin{document}
\def\a{A}
\def\b{B}
\def\c{CC}
\expandafter\def\csname\a\b\endcsname{\c}
\AB
\end{document}
将会按照您的期望进行。
我最喜欢阅读的是\expandafter
:教程\expandafter
(拖船 9)
答案2
控制序列\a
、\b
和\c
已经在 LaTeX 2ε 内核中定义。
\MYa
,\MYb
并将\MYc
改用。
为了更好地体验 (La)TeX 的扩展机制,我提供了以下方法:
使用这种方法\csname
可以触发扩展直到找到匹配项,并与诱骗 LaTeX 扩展 -construct 后面的内容\endcsname
结合使用:\expandafter
\csname..\endcsname
% Use LaTeX for compiling this:
\newcommand*\MYa{A}
\newcommand*\MYb{B}
\newcommand*\MYc{CC}
\expandafter\newcommand\csname\MYa\MYb\expandafter\endcsname\expandafter{\MYc}
\show\AB
\stop
您还可以\expandafter
结合\exchange
:
% Use LaTeX for compiling this:
\newcommand\exchange[2]{#2#1}
\newcommand*\MYa{A}
\newcommand*\MYb{B}
\newcommand*\MYc{CC}
\expandafter\exchange\expandafter{\expandafter{\MYc}}{\expandafter\newcommand\csname\MYa\MYb\endcsname}%
\show\AB
\stop
在许多情况下,添加花括号的变体\exchange
很有用:
% Use LaTeX for compiling this:
\newcommand\PassFirstToSecond[2]{#2{#1}}
\newcommand*\MYa{A}
\newcommand*\MYb{B}
\newcommand*\MYc{CC}
\expandafter\PassFirstToSecond\expandafter{\MYc}{\expandafter\newcommand\csname\MYa\MYb\endcsname}%
\show\AB
\stop
还有一个宏\name
,它将一个参数的第一对花括号之前的所有内容以及另一个参数的第一对花括号内的所有内容应用\csname..\endcsname
到花括号内的内容后返回内容:
\name\newcommand{macro}...
→ \newcommand\macro...
\name\global\long\def{macro}...
→ \global\long\def\macro...
\name\string{macro}
→ \string\macro
\name{macro}
→ \macro
\name\name\let{macroA}={macroB}
→ \name\let\macroA={macroB}
→\let\macroA=\macroB
% Use LaTeX for compiling this:
\newcommand*\MYa{A}
\newcommand*\MYb{B}
\newcommand*\MYc{CC}
\newcommand\exchange[2]{#2#1}%
\csname @ifdefinable\endcsname\name{%
\long\def\name#1#{\romannumeral0\innername{#1}}%
}%
\newcommand\innername[2]{%
\expandafter\exchange\expandafter{\csname#2\endcsname}{ #1}%
}%
\name\expandafter\newcommand\expandafter{\MYa\MYb}\expandafter{\MYc}%
% ->
% \romannumeral0\innername{\expandafter\newcommand\expandafter}{\MYa\MYb}\expandafter{\MYc}
% ->
% %\romannumeral0-expansion in progress
% \expandafter\exchange\expandafter{\csname\MYa\MYb\endcsname}{ \expandafter\newcommand\expandafter}\expandafter{\MYc}%
% -> ( \expandafter-chain "hits" \csname )
% %\romannumeral0-expansion in progress
% \exchange{\AB}{ \expandafter\newcommand\expandafter}\expandafter{\MYc}%
% ->
% %\romannumeral0-expansion in progress
% <space>\expandafter\newcommand\expandafter\AB\expandafter{\MYc}%
% ->
% %\romannumeral0-expansion terminated:
% \expandafter\newcommand\expandafter\AB\expandafter{\MYc}%
% -> ( \expandafter-chain "hits" \MYc )
% \newcommand\AB{CC}%
\show\AB
\stop
如果\PassFirstToSecond
有的话:
% Use LaTeX for compiling this:
\newcommand*\MYa{A}
\newcommand*\MYb{B}
\newcommand*\MYc{CC}
\newcommand\PassFirstToSecond[2]{#2{#1}}%
\newcommand\exchange[2]{#2#1}%
\csname @ifdefinable\endcsname\name{%
\long\def\name#1#{\romannumeral0\innername{#1}}%
}%
\newcommand\innername[2]{%
\expandafter\exchange\expandafter{\csname#2\endcsname}{ #1}%
}%
\expandafter\PassFirstToSecond\expandafter{\MYc}{\name\newcommand*{\MYa\MYb}}%
\show\AB
\stop
我最喜欢阅读\expandafter
(以及如何保持简短\expandafter
)的是线程“如何知道附加到 csname 宏时的 expandafter 数量”。 ;-)