将嵌套参数说明符传递给钩子

将嵌套参数说明符传递给钩子

考虑一个宏

  • 声明一些其他文档命令,但通过接受他们的主体作为自己的参数(简单间接)
  • 还将自身的调用添加到某个钩子中

如同

\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.

相关内容