在命令定义中将 #1 替换为其他内容

在命令定义中将 #1 替换为其他内容

在开发版本的软件包中cleveref-usedon,有一个命令\SetUsedByAndOnMessageStyle,目前简单定义为

\NewDocumentCommand \SetUsedByAndOnMessageStyle { m }
  {
    \cs_set:Nn \__UsedByAndOnMessage:nn { #1 }
  }

这样当用户写类似的东西时

\SetUsedByAndOnMessageStyle
  {%
    \todo{%
      \UsedByAndOnMessageText{#1}{#2}%
    }%
  }

内部命令\__UsedByAndOnMessage:nn应定义为指定的内容。但是,我希望用户能够编写

\SetUsedByAndOnMessageStyle
  {%
    \todo{%
      #1% <--- Changed line
    }%
  }

并让命令\SetUsedByAndOnMessageStyle将其替换#1\UsedByAndOnMessageText{#1}{#2}。这可能吗?(例如,通过一些辅助宏?)


下面是 MWE。

\documentclass{article}
\usepackage{xcolor}
\ExplSyntaxOn
% Some preset for testing
\cs_new:Nn \__UsedByAndOnMessage:nn { }
\NewDocumentCommand \UsedByAndOnMessageText { m m }
  {
    \begin{enumerate}
      \item #1
      \item #2
    \end{enumerate}
  }

% This is the command to be improved
\NewDocumentCommand \SetUsedByAndOnMessageStyle { m }
  {
    \cs_set:Nn \__UsedByAndOnMessage:nn { #1 }
  }

\NewDocumentCommand \test { m m }
  {
    \__UsedByAndOnMessage:nn { #1 } { #2 }
  }

\ExplSyntaxOff

\begin{document}

\SetUsedByAndOnMessageStyle{
  \color{red}
  % \UsedByAndOnMessageText{#1}{#2} % This generates the ideal result
  #1
}

\test{a}{b}

\end{document}

答案1

这是你想要的嗎?

\ExplSyntaxOn
\NewDocumentCommand \SetUsedByAndOnMessageStyle { m }
  {
    \cs_set:Npn \__UsedByAndOnMessage:nn  ##1 { \cs_set:Nn \__UsedByAndOnMessage:nn { #1 } }
    \__UsedByAndOnMessage:nn { \UsedByAndOnMessageText{##1}{##2} } 
  }
\SetUsedByAndOnMessageStyle
  {
    \todo{ #1  }
  }

\cs_show:N \__UsedByAndOnMessage:nn 

\SetUsedByAndOnMessageStyle{
  \color{red}
  % \UsedByAndOnMessageText{#1}{#2} % This generates the ideal result
  #1
}

\cs_show:N \__UsedByAndOnMessage:nn 

\stop

控制台输出:

pdflatex test.tex
This is pdfTeX, Version 3.14159265-2.6-1.40.21 (TeX Live 2020) (preloaded format=pdflatex)
 restricted \write18 enabled.
entering extended mode
(./test.tex
LaTeX2e <2020-10-01> patch level 4
L3 programming layer <2021-02-18>
> \__UsedByAndOnMessage:nn=\long macro:#1#2->\todo {\UsedByAndOnMessageText
{#1}{#2}}.
<recently read> }
                 
l.13 \cs_show:N \__UsedByAndOnMessage:nn
                                        
? 
> \__UsedByAndOnMessage:nn=\long macro:#1#2->\color
{red}\UsedByAndOnMessageText {#1}{#2}.
<recently read> }
                 
l.21 \cs_show:N \__UsedByAndOnMessage:nn
                                        
? 
 )
No pages of output.
Transcript written on test.log.

尝试给出一些解释:

乍一看可能会令人困惑,但您可以像任何其他非 -token 一样将哈希()作为宏参数的组成部分。#6\outer

乍一看,事情似乎很微妙,需要在嵌套宏定义中挑剔“哈希加倍”和在扩展宏时挑剔“哈希折叠”:

在展开宏时,一些标记来自定义宏时提供的宏的 ⟨replacement text⟩。其他标记来自展开宏时收集的宏的参数。

如果在定义宏时在 ⟨替换文本⟩ 中提供了两个连续的哈希值,那么在扩展宏时,这两个连续的哈希值会折叠成一个哈希值。

如果在 TeX 中定义\def\macro{\string##},则扩展\macro会产生\string #

如果在 expl3 中定义\cs_new:Npn \macro{\token_to_str:N ##},则展开\macro可得到\token_to_str:N #

这种“哈希值崩溃”就是为什么在定义内嵌套定义时可以/需要双重哈希值的原因。但刚刚提供的小定义表明,“哈希值崩溃”并不局限于在定义内嵌套定义。

需要注意的是,这种哈希折叠仅适用于定义时提供的⟨替换文本⟩的哈希。它不适用于在扩展宏时作为宏参数的组件收集的哈希。

通过\SetUsedByAndOnMessageStyle您希望提供一个宏参数,该参数将形成宏的⟨替换文本⟩ \__UsedByAndOnMessage:nn,而宏又需要两个参数。
使用该⟨替换文本⟩参数,您希望#1将其替换为\UsedByAndOnMessageText{#1}{#2}

因此,第一步的想法是定义(通过\cs_new:Npn,您可以在其中指定p独立于要定义的宏名称签名的参数文本)的“临时变体” \__UsedByAndOnMessage:nn,尽管签名:nn只处理一个参数 -#1您提供的\SetUsedByAndOnMessageStyle“⟨替换文本⟩参数” - 并且重新定义自身以处理两个参数。

在第二步中,使用\UsedByAndOnMessageText{#1}{#2}作为其参数来调用 scratch-variant,现在将其转换为 ⟨replacement text⟩ 而不是#1

例如,

\SetUsedByAndOnMessageStyle
  {
    \todo{ #1  }
  }

使用\SetUsedByAndOnMessageStyle的参数#1= \todo{ #1 },并考虑到 的\SetUsedByAndOnMessageStyle⟨替换文本⟩ 中的两个连续哈希折叠成一个哈希,得出:

\cs_set:Npn \__UsedByAndOnMessage:nn  #1 { \cs_set:Nn \__UsedByAndOnMessage:nn { \todo{ #1  } } }
\__UsedByAndOnMessage:nn { \UsedByAndOnMessageText{#1}{#2} } 

,因此第二行,

\__UsedByAndOnMessage:nn { \UsedByAndOnMessageText{#1}{#2} }

产量

\cs_set:Nn \__UsedByAndOnMessage:nn { \todo{ \UsedByAndOnMessageText{#1}{#2}  } }

相关内容