Knuth 在 TeXbook 的练习 20.5 中隐藏了分隔参数的特殊规则。
如果参数文本的最后一个字符是 #,并且这个 # 后面紧跟着 {,那么 TeX 的行为就像是 { 已插入到参数文本和替换文本的右端一样。
这意味着宏可以定义为,
\def\a#1#{#1}
将其调用为\a 10
将得到runaway argument error
,而将其调用为\a{12}
,将毫无问题地进行编译。
即使在查看了 TeX by Topic、TeXbook 和 LaTeX 源之后,我仍然很难找到此类宏的实际应用。
这是我的想法,创建一些命令来排版并进行一些分数计算,例如在基础算术教科书中找到的那些。你输入这个,
\[\FRAC ADD{3}{8}+{1}{7}\]
\[\FRAC SUB{5}{8}-{1}{7}\]
\[\FRAC MUL{5}{8}x{13}{1201}\]
你会得到这个:
这是代码,
\documentclass{article}
\begin{document}
%% macro factory
\def\FRAC#1#{\csname #1\endcsname}
%% add
\def\ADD#1#2+#3#4{%
\frac{#1}{#2}+\frac{#3}{#4}=
\frac{\the\numexpr(#4*#1)+(#3*#2)}{\the\numexpr#2*#4}
}
%% subtract
\def\SUB#1#2-#3#4{%ok top
\frac{#1}{#2}-\frac{#3}{#4}=
\frac{\the\numexpr(#4*#1)-(#3*#2)}{\the\numexpr(#2*#4)}
}
%% multiply
\def\MUL#1#2x#3#4{%
\frac{#1}{#2}\times\frac{#3}{#4}=
\frac{\the\numexpr(#1*#3)}
{\the\numexpr#2*#4}
}
%% testing
\[\FRAC ADD{3}{8}+{1}{7}\]
\[\FRAC SUB{5}{8}-{1}{7}\]
\[\FRAC MUL{5}{8}x{13}{1201}\]
\end{document}
此类宏有任何实际应用吗?是否需要采取任何特殊预防措施?Knuth 为什么要首先包含此功能?
答案1
这种捕捉直到第一个打开的括号的技巧可以在很多情况下使用。
正如其他发帖者所说,它允许可扩展地捕获可选参数(如果它不是最后一个)。原则上可以让它完全不受嵌套影响,但尚未实现。
它可用于解析用户提供的定义主体,以将其更改为适合您的目的,同时保持自然语法。(有关解析 def 的更多信息,请参见下文。)
我主要使用这个技巧来解析一个可扩展的标记列表,而不会丢失任何括号。例如,
将给定的字符串扩展为大写或小写(参见这个答案)
完全扩展一个 token 列表(几乎和 luatex 原语一样好
\expanded
(参见这个答案)选择性地扩展 token,或者相反的顺序
编写一个原始的宏扩展器(即获取一个文件并扩展用户定义的宏)
因此基本上任何情况下都需要小心使用大括号,但不能使用
\futurelet
。
此技巧仅在只有一个字符具有 catcode 1(起始组字符)时才有效。此外,我们需要能够在我们正在操作的标记列表的末尾放置一个标记:否则,在没有左括号的情况下,我们将得到一个失控参数。
关于解析定义:假设您想为用户提供一种简单的方法来定义一个宏,该宏可能需要参数,并且始终产生带框的数学结果。假设您还希望参数文本是任意的。要么让用户做所有事情,要么使用您询问的技巧来解析定义。
%non-user-friendly
\def\foo_#1^#2#3#4{\fbox{$\sum_{#1}^{#2} \frac{#3}{#4}$}}
%more user friendly (perhaps)
\boxeddef\foo_#1^#2#3#4{\sum_{#1}^{#2} \frac{#3}{#4}}
要做到这一点:
\documentclass{article}
\makeatletter
\def\boxeddef#1#2#{\boxeddef@aux{#1}{#2}}
\def\boxeddef@aux#1#2#3{\def#1#2{\fbox{$#3$}}}
\makeatother
\begin{document}
%\def\foo#1#2#3#4{\fbox{$\sum_{#1}^{#2} \frac{#3}{#4}$}}
\boxeddef\foo_#1^#2#3#4{\sum_{#1}^{#2} \frac{#3}{#4}}
\[
\foo_{a}^{b}{C}{D}
\]
\end{document}
答案2
这个#{
技巧可以制作出类似这样的宏\hbox{...}
,例如,它们可以在参数中逐字逐句地进行操作(而且,参数不会被预先读取)。 Tugboat 文章中有一个很好的例子TeX 层次结构(1994年第15卷,第7-9页):
与巫师的版本相比,Guru 的使用#{
确保了被吃掉的字符\let\next=
始终是括号,而不是其他东西。
答案3
来自 latex.ltx 的一个例子
\def\usepackage#1#{%
\@latex@error
{\noexpand \usepackage before \string\documentclass}%
{\noexpand \usepackage may only appear in the document
preamble, i.e.,\MessageBreak
between \noexpand\documentclass and
\string\begin{document}.}%
\@gobble}
使用参数设置,您可以处理可选参数,例如\usepackage[foo]{bar}
无需定义不同的情况
答案4
我刚刚在可扩展消毒器中使用了这种结构:是否可以定义一个可扩展的命令来从其参数中删除控制序列?。这样做的目的是,我可以解析它包含的第一个组的任意(格式正确的)输入,而无需实际进入该组。如果没有\futurelet
或\catcode
更改,就不可能抓取单个括号标记,但这样,我可以扩展直到......为止一个组,将括号留在那里,然后继续处理,确信菜单上的下一个项是一个组。它的目的与\futurelet
此一个标记完全相同。