我刚刚玩了几个小时的 expl3。很多功能都比 2e 方便得多。有些功能可以实现我认为 LaTeX 无法实现的功能。但是,我缺少一个功能。
我很想有一个函数,也许execute_after:NN <macroA> <macroB>
先执行宏 A,让它从输入流中获取尽可能多的标记,然后执行宏 B。因此,假设\macroA
和\macroB
都接受 2 个参数,\execute_after:NN \macroA \macroB abcdefg
则相当于\macroA{a}{b}\macroB{c}{d}efg
。
答案1
“简单”情况:参数定义为宏的参数\macroA
正式的宏参数在宏的定义(参数文本)中指定,例如
\def\foo[#1]#2{...}
它定义了一个宏,foo
其参数文本[#1]#2
包含两个参数,#1
,以 分隔,]
以及一个未分隔的参数#2
。有两种方法可以了解宏的参数文本\foo
:
应用于
\meaning
宏会扩展为包含其定义的字符串。\meaning\foo
扩展为macro:[#1]#2->...
。但是这是一个纯文本字符串,字符是字符标记,catcode 为 12(其他),但空格字符除外,其 catcode 为 10(空格)。因此,有关原始标记和 catcode 的信息丢失了。\catcode
\[=
\$ \catcode\]=
\& \def\funny[#1]#2{...}定义一个宏
\funny
,其中[
和]
具有不寻常的 catcode。但是\meaning\funny
扩展为与完全相同的字符串\meaning\foo
。可以定义一个宏,并将其与未知宏进行比较。如果测试为
\ifx
真,则参数文本已知。
由于 的结果\meaning
是有限的,因此可以遍历所有字节/标记/catcode 组合来定义所有宏,其\meaning
扩展为完全相同的字符串。然后可以通过 进行比较来找到正确的宏\ifx
。
然后\execute_after:NN
可以根据宏的参数文本解析输入流以获取参数,使用该参数调用宏并\macroB
随后插入。
然而它的用途非常有限:
- 枚举所有可能的宏定义并非易事。
- 效率:太糟糕了。
通常,“参数”在稍后的扩展或执行步骤中被读取,例如:
\section
正式定义为无参数宏。它扩展为\@startsection
执行某些操作并最终检查星号标记,查找可选参数和...“参数”定义不明确。例如,
\begin
有一个参数。它调用可以有更多参数的启动环境宏。此外,还有一个带有结束标记的环境主体\end{...}
。\macroB
在\begin{tabular}
或之后插入\begin{verbatim}
可能不是最佳选择。
执行\macroA
\execute_after:NN
可以将宏存储\macroB
在某处并执行\macroA
。但那\execute_after:NN
已成历史,没有代码可以执行存储的\macroB
。请参阅 egreg 的注释。
分析执行情况\macroA
无需执行,\execute_after:NN
就可以检查宏定义\macroA
并进一步分析宏,以及执行时可能读取的参数。由于 TeX 语言的图灵完备性,因此很可能存在这样的分析算法(Rice 定理/停机问题)。
什么是可能的?
在两种情况下,TeX 允许在将来插入代码:
\aftergroup
记住当前组结束后立即调用的标记。它可以被调用多次,并且标记将按照执行顺序附加\aftergroup
。\afterassignment
记住一个标记,该标记在下一次赋值后立即执行。后续执行将\afterassignment
覆盖记住的标记。
这两种情况都不能在这里应用,因为它会限制\macroA
为结束当前组的宏或在最后一步执行一个分配。