考虑一个宏
- 声明一些其他文档命令,但通过接受他们的主体作为自己的参数(简单间接)
- 还将自身的调用添加到某个钩子中
如同
\NewDocumentCommand{ \DeclareStuff } { m }
{
\DeclareDocumentCommand { \Select } { m m } { #1 }
\AddToHook { hook } { \DeclareStuff { #1 } }
}
调用的\DeclareStuff { #1 }
效果是,现在\Select{one}{two}
扩展为one
。然而,hook
现在包含令牌列表
\DeclareStuff { ##1 }
我明白这里发生了什么:
- 扩展时
\DeclareStuff { #1 }
,我们用#
双精度型替换##
,并将的定义\DeclareStuff
放入输入流中。 - 因此,在宏主体中找到时
#1
将扩展为##1
- 当
\DeclareDocumentCommand { \Select } { m m }
搜索其宏体时,它将首先看到#1
,从而##1
被放置在输入流中,然后#1
再次扩展为 (消除双精度#
)作为收到的参数。 - 所以这相当于仅仅声明
\DeclareDocumentCommand { \Select } { m m } { #1 }
(本质上是重新定义\use_i:nn
)
到目前为止,这就是我想要的/期望的,但是
- 当处理
\AddToHook
并遇到时#1
,我们将##1
像以前一样用 替换它,但不会进一步扩展它,因为显然钩子不会进一步扩展它(为什么?) - 这导致了上述定义
实际上,我希望能够稍后执行这个钩子,从而再次恢复相同的定义。
我的问题是:#1
宏“主体”中遇到的两次扩展有什么区别\DeclareStuff
?我该怎么做才能使执行钩子具有与\DeclareStuff
当前正在扩展的宏完全相同的效果,也就是说,我如何保存宏的“当前‘调用’”以供以后重新使用?
一些与问题实际上不相关的背景信息:
- 我想这样做是因为钩子实际上必须将这些信息(以稍微修改的方式)写入文档末尾的辅助文件中,以便在下次运行 LaTeX 时,我可以重用这些定义。
- 原因是在
\DeclareStuff
从其他地方调用宏之前涉及相当多的计算,我可以通过直接使用在第二次运行时保存这些计算\DeclareStuff
。 - 执行这些计算并最终调用的宏
\DeclareStuff
当然use_none:..
在第二次运行 LaTeX 时被重新定义,为此我在 aux 文件中设置了一个标志
答案1
重复#
只是#
替换文本显示方式的一种产物,如果您运行钩子,您会看到有效定义与直接使用相同。
这\Select
按照您描述的方式定义,然后将其定义为\relax
然后再次运行钩子将其定义为再次具有原始定义。
\documentclass{article}
\begin{document}
\ExplSyntaxOn
\NewHook{hook}
\NewDocumentCommand{ \DeclareStuff } { m }
{
\DeclareDocumentCommand { \Select } { m m } { #1 }
\AddToHook { hook } { \DeclareStuff { #1 } }
}
\ExplSyntaxOff
\DeclareStuff{#1}
\ShowCommand\Select
\Select{one}{two}
\DeclareDocumentCommand { \Select } {}{zz}
\ShowCommand\Select
\UseHook{hook}
\ShowCommand\Select
\Select{one}{two}
\end{document}
答案2
我们来看看这个事情:
\NewDocumentCommand{ \DeclareStuff } { m }
{
\DeclareDocumentCommand { \Select } { m m } { #1 }
\AddToHook { hook } { \DeclareStuff { #1 } }
}
如果你这样做\DeclareStuff{<Definition text>}
,你会得到:
\DeclareDocumentCommand { \Select } { m m } { <Definition text> }
\AddToHook { hook } { \DeclareStuff { <Definition text> } }
在扩展定义文本包含连续井号的宏时,减少连续井号与此无关。
\edef\macro{\unexpanded{...}}
通过使用或通过使用\edef\macro{\the<token register>}
(或通过使用)将最终位于宏定义中的哈希值加倍,\xdef\macro...
以补偿在扩展相关宏时哈希值减少的情况,这在这里也不相关。
当写入外部文本文件或屏幕(例如,通过\show
或通过\immediate\write-1{...}
或通过\message{...}
)或执行\detokenize
或时哈希值的加倍\scantokens
与此无关。
关键点是:
您\DeclareStuff
将其自己的调用添加到钩子中。
因此,如果钩子被执行,\DeclareStuff
就会被执行,从而再次将其自身的调用添加到钩子中。
这样,每次调用钩子时,\DeclareStuff
钩子中存储的数量就会翻倍。
您可以通过引入一个布尔值来避免这种情况,该布尔值指示是否\DeclareStuff
应将内容添加到钩子中,并在钩子内部添加一个指令来设置布尔值,这样就不会将内容添加到钩子中。如果这样做,您必须确保该指令始终是钩子执行的第一件事。
\documentclass{article}
\ExplSyntaxOn
\bool_new:N \g__mymodule_AddToMyHook_bool
\NewHook{MyHook}
\cs_new:Npn \AddToMyHooktrue {\bool_gset_true:N \g__mymodule_AddToMyHook_bool}
\cs_new:Npn \AddToMyHookfalse {\bool_gset_false:N \g__mymodule_AddToMyHook_bool}
\AddToHook{MyHook}{\AddToMyHookfalse}
\AddToMyHooktrue
\NewDocumentCommand{ \DeclareStuff } { m }
{
\DeclareDocumentCommand { \Select } { m m } { #1 }
\bool_if:NT \g__mymodule_AddToMyHook_bool {
\AddToHook { MyHook } { \DeclareStuff { #1 } }
}
}
\ExplSyntaxOff
\begin{document}
% This defines \Select and appends the definition to the hook:
\AddToMyHooktrue
\DeclareStuff {Argument 1 is: #1. Argument 2 is: #2.}
\expandafter\show\csname Select code\endcsname
% This defines \Select but does not append the definition to the hook:
\AddToMyHookfalse
\DeclareStuff {ArGuMeNt 1 is: #1. ArGuMeNt 2 is: #2.}
\expandafter\show\csname Select code\endcsname
% This redefines \Select as often as \DeclareStuff was invoked while
% \AddToHookstrue was in effect. In the end the last \DeclareStuff
% invoked while \AddToHookstrue was in effect makes the definition
% of \select:
\UseHook{MyHook}
\expandafter\show\csname Select code\endcsname
\end{document}
控制台输出为:
$ pdflatex-dev test.tex
This is pdfTeX, Version 3.14159265-2.6-1.40.21 (TeX Live 2020) (preloaded format=pdflatex-dev)
restricted \write18 enabled.
entering extended mode
(./test.tex
LaTeX2e <2021-05-01> pre-release-1 (develop 2021-2-27 branch)
L3 programming layer <2021-02-18>
(/usr/local/texlive/2020/texmf-dist/tex/latex-dev/base/article.cls
Document Class: article 2020/11/23 v1.4m Standard LaTeX document class
(/usr/local/texlive/2020/texmf-dist/tex/latex-dev/base/size10.clo))
(/usr/local/texlive/2020/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def)
(./test.aux)
> \Select code=\protected\long macro:
#1#2->Argument 1 is: #1. Argument 2 is: #2..
<recently read> \Select code
l.60 ...andafter\show\csname Select code\endcsname
?
> \Select code=\protected\long macro:
#1#2->ArGuMeNt 1 is: #1. ArGuMeNt 2 is: #2..
<recently read> \Select code
l.65 ...andafter\show\csname Select code\endcsname
?
> \Select code=\protected\long macro:
#1#2->Argument 1 is: #1. Argument 2 is: #2..
<recently read> \Select code
l.73 ...andafter\show\csname Select code\endcsname
?
(./test.aux) )
No pages of output.
Transcript written on test.log.