以 # 作为最后一个参数的宏

以 # 作为最后一个参数的宏

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此一个标记完全相同。

相关内容