![如何在宏定义中 \expandafter a \let#1](https://linux22.com/image/465237/%E5%A6%82%E4%BD%95%E5%9C%A8%E5%AE%8F%E5%AE%9A%E4%B9%89%E4%B8%AD%20%5Cexpandafter%20a%20%5Clet%231.png)
有时我想重新定义一些CS在一个范围内,通常是宏定义。例如:
\def[[cs名称>]]{ [[重新定义一些 cs]] [[该宏的实际功能]] [[恢复重新定义的 cs]] }
主要需要做的是定义Redefine
和的宏Recover
,我们将它们称为\redefine
和\recover
。
对于恢复来说,几乎不存在冲突CS应该用来存储CS要重新定义,并且它们的名称应该是相对的。这里我使用“其 csname 的 csname”:
\def\redefine#1#2{
\expandafter \let \csname\string#1\endcsname #1 \relax
\def#1{#2}
}
哪儿#1
是CS被重新定义。
现在发现,原来的意思已经无法挽回了。
\def\recover#1{
\let #1 \csname\string#1\endcsname
}
在 之前似乎\csname\string#1\endcsname
无法展开\let
,因为#
1
是两个不同的标记,但当且仅当它们连续出现时,在宏定义中将其视为参数替换。同时\expandafter
只跳过 1 个标记。
也许,\def
但\let
在大多数情况下不能使用,我仍然想知道当第一个标记是参数时,有什么方法可以使第二个标记在第一个标记之前扩展?
IE
定义了一些较短的原始 cs:
\let\epaf\expandafter
\let\str\string
\let\nep\noexpand
\let\cs\csname
\let\sc\endcsname
...
一个宏\mfont
,解析一个句子直到\relax
,并设置大量的字体属性,并且使单个cs同时设置拉丁字体和unicode字体。它使用大量的原始cs来解析句子,如果保留长名称,定义很难阅读。
\mfont
在从到的块之后\relax
,应该恢复 shoter 名称。\mfont
应该为全局定义字体,但较短的名称应该仅在范围内有效\mfont
。
答案1
你有虚假的空间,并且缺失\expandafter
% no space!!!!
\def\redefine#1#2{%
\expandafter \let \csname\string#1\endcsname #1%
\def#1{#2}%
}
% no space!!!!
\def\recover#1{%
% missing \expandafter
\expandafter \let \expandafter #1\csname\string#1\endcsname
}
{\tt \string\sin=\meaning\sin}
\redefine\sin{hello}
{\tt \string\sin=\meaning\sin}
\recover\sin
{\tt \string\sin=\meaning\sin}
\bye
答案2
除了虚假的空间,你的\recover
代码
\let #1 \csname\string#1\endcsname
如果你打电话,\recover\foo
你会得到
\let\foo\csname\string\foo\endcsname
因此,您要将其重新定义\foo
为\csname
,打印\foo
(作为字符串)并收到有关位置错误的错误消息\endcsname
。
你需要
\expandafter\let\expandafter#1\csname\string#1\endcsname
所以形成了 token前 \let
开始行动。
替代方法,您无需担心虚假空格和\expandafter
。
参数类型c
表示“形成一个控制序列标记”(它\csname...\endcsname
在内部使用并应用正确的\expandafter
序列。也\cs_set:Npn
相当于\def
(实际上\long\def
,但在这种情况下并不重要)。
\input expl3-generic
\ExplSyntaxOn
\cs_new_protected:Npn \redefine #1 #2
{
% save the current meaning of #1
\cs_set_eq:cN { \token_to_str:N #1 } #1
\cs_set:Npn #1 { #2 }
}
\cs_new_protected:Npn \recover #1
{
\cs_set_eq:Nc #1 { \token_to_str:N #1 }
}
\ExplSyntaxOff
{\tt \string\sin=\meaning\sin}
\redefine\sin{hello}
{\tt \string\sin=\meaning\sin}
\recover\sin
{\tt \string\sin=\meaning\sin}
\bye
如果你不想走这条路,你可以也应该定义自己的语法糖。expl3
但很多及其\cs_set_eq:NN
变体。
\def\cslet#1{\expandafter\let\csname#1\endcsname}
\def\letcs#1#2{\expandafter\let\expandafter#1\csname#2\endcsname}
\def\redefine#1#2{%
% save the current meaning of #1
\cslet{\string#1}#1%
% redefine #1
\def#1{#2}%
}
\def\recover#1{\letcs#1{\string#1}}
{\tt \string\sin=\meaning\sin}
\redefine\sin{hello}
{\tt \string\sin=\meaning\sin}
\recover\sin
{\tt \string\sin=\meaning\sin}
\bye
答案3
为了应对隐式空间,\let
我建议这样做:
\def\redefine#1{%
\expandafter\futurelet\csname\string#1\endcsname\def#1%
}%
\def\exchange#1#2{#2#1}%
\def\recover#1{%
\expandafter\exchange\expandafter{\csname\string#1\endcsname}{\let#1= }%
}%
\def\foobar{foo}
\foobar
\redefine\foobar{bar}
\foobar
\recover\foobar
\foobar
\bigskip
Doing the implicit space game:
\bigskip
\exchange{ }{\let\foobar= }%
A\foobar A
\redefine\foobar{bar}
B\foobar B
\recover\foobar
C\foobar C
\bye
或者只是通过\begingroup..\endgroup
或进行一些范围界定{..}
:
\def\foobar{foo}%
\foobar
\begingroup
\def\foobar{bar}%
\foobar
\endgroup
\foobar
{%
\def\foobar{bar}%
\foobar
}%
\foobar
\bye