我经常发现自己需要扩展一些宏,这些宏的内容应该作为另一个命令的参数。通常的解决方案是调用一些\expandafter
混合物以某种方式控制扩展,但我想知道(至少对于某些常见情况)是否存在更简单的方法。
举个例子,假设我想写如下内容
\somecommand{Some Argument}{\secondarg}
但我想扩大\secondarg
前实际上正在尝试处理\somecommand
。
对于这种情况,是否可以定义一个命令以便
\expandafterallthat{\somecommand{Some Argument}}{\secondarg}
扩展为类似
\somecommand{SomeArgument}{Contents of secondarg macro}
答案1
这是由etextools
以下提供的\expandnext
:
\usepackage{etextools}
\expandnext{\somecommand{Some Argument}}{\secondarg}
但是如果你只想扩展第二个参数,那么你可以使用双参数交换,这是我在我的一些包中使用的技巧:
\documentclass{article}
%% Implementation
\def\myswap#1#2{#2{#1}}
\def\expandafterallthat#1#2{%
\expandafter\myswap\expandafter{#2}{#1}%
}
%%%%%%%%%%%%%%%%
% Test case:
%
\makeatletter
\def\somecommand#1#2{%
\def\first{#1}%
\@onelevel@sanitize\first
\par\texttt{First argument: \first}%
\def\second{#2}%
\@onelevel@sanitize\second
\par\texttt{Second argument: \second}%
}
\makeatother
\def\secondarg{\empty Some stuff\empty}
\begin{document}
\expandafterallthat{\somecommand{\empty Some Argument\empty}}{\secondarg}
\end{document}
这正确打印:(s\empty
用于查看参数是否进一步扩展)
first argument: \empty Some Argument\empty
Second argument: \empty Some stuff\empty
答案2
不使用包进行编写
\documentclass{article}
\newcommand\expandafterallthat[2]{%
\expandafter\expandafterallthataux\expandafter{\romannumeral -`0#2 }{#1}%
}
\newcommand\expandafterallthataux[2]{#2{#1}}
\newcommand\somecommand[2]{\detokenize{#1:::#2}}
\newcommand\secondarg{\thirdarg}
\newcommand\thirdarg{\fourtharg}
\newcommand\fourtharg{example}
\begin{document}
\expandafterallthat{\somecommand{Some Argument}}{\secondarg}
\end{document}
将多次扩展输入。(您没有说明是扩展#2
一次还是多次。)
使用最新的引擎版本(MiKTeX 或即将推出的 TL'19),可以使用\expanded
:
\documentclass{article}
\newcommand\expandafterallthat[2]{%
\expanded{\unexpanded{#1}{#2}}}
\newcommand\somecommand[2]{\detokenize{#1:::#2}}
\newcommand\secondarg{\thirdarg}
\newcommand\thirdarg{\fourtharg}
\newcommand\fourtharg{example}
\begin{document}
\expandafterallthat{\somecommand{Some Argument}}{\secondarg}
\end{document}
(\expanded
一直在 LuaTeX 中,因此即使没有最新更新的引擎,您也可以在那里进行测试。)
答案3
由于 Joseph 尚未提及expl3
,因此让我来提及:LaTeX3 软件包包含一个用于扩展控制的模块。l3expan
具体而言,这包括用于扩展命令参数的函数,其形式\exp_args:N...
为点为字母,对应于应如何处理各种参数。
有许多字母在使用:n
表示应不加修改地传递的括号组、o
扩展一次、f
从左侧完全扩展,在第一个不可扩展的标记处停止、x
完全扩展整个标记,就像 中一样\edef
。还有N
表示应按原样传递的单个非括号标记、c
表示应制成控制序列然后作为参数处理的文本N
,以及V
表示v
变量(有关详细信息,请参阅 l3expan 文档)。
在这里,您想要扩展第二个参数一次,而保持第一个参数不变,因此您需要的变体是\exp_args:Nno
:保持函数不变(N
),第一个参数用括号括起来,不变(n
),第二个参数扩展一次(o
)。
\newcommand{\foo}[2]{\showtokens{#1... #2}}
\newcommand{\sometext}{Some text.}
\RequirePackage{expl3}
\ExplSyntaxOn
\exp_args:Nno \foo {\error} {\sometext}
\ExplSyntaxOff
答案4
和functional
包中,棘手的参数扩展可以用直观的函数组合来代替,这与其他编程语言类似,例如Lua
:
\documentclass{article}
\usepackage{functional}
\Functional{scoping=true} % make every function become a group
\begin{document}
\IgnoreSpacesOn
\PrgNewFunction \SomeFunction { m m m } {
\TlLog {#1}
\TlLog {#2}
\TlLog {#3}
}
\IgnoreSpacesOff
\newcommand\secondarg{Second Argument}
\newcommand\thirdarg{Third Argument}
\SomeFunction{First Argument}{\Value\secondarg}{\Value\thirdarg}
\end{document}
上面的代码中,\Value
是一个预定义的函数。日志文件中的结果如下:
> First Argument.
> Second Argument.
> Third Argument.