我正在开发一款 LaTeX 漂亮打印机,它可以解析 LaTeX 文档,然后重新渲染源代码。(我知道这并非在所有情况下都可行,但很多时候应该是可行的。)
本着Prettier,我希望代码格式化程序具有主见,并且我正在尝试决定哪些宏应该将其参数自动包装在组 {} 中。例如,\mathbb R
应该转换为\mathbb{R}
,并且\frac23
应该转换为\frac{2}{3}
。
我的问题是:(a)是否存在将一组参数传入 LaTeX 宏会导致意外行为的情况?(b)是否存在将参数包装在组中视为“最佳实践”的宏?
后续问题是:您会将哪些内容放入常用的“特殊”宏列表中。这可能包括不遵循标准约定(of[...]
和{...}
style 参数)的 LaTeX 宏,或需要特殊处理的常用宏(例如几乎所有蒂克兹环境...)。
为了控制范围,我不是尝试解析复杂的 TeX 编程或宏定义本身。
答案1
所有 TeX 宏都在其参数周围使用括号组,如果参数是单个标记,则可以省略括号组,尽管在分隔参数的情况下,该组可能不在您想要的位置。
\def\foo#1#2{...}
则\foo12
,\foo 1 2
,\foo{1}{2}
,\foo{1} {2}
都是等价的。
\def\foo(#1,#2){...}
则\foo(1,2)
,\foo({1},{2})
,\foo(1,{2})
, 都是等价的。
TeX 基元没有这种可选的括号。
\hbox{a}
括号是必需的,\hbox a
这是语法错误,但是括号可以是隐式的,\hbox\bgroup a}
这是可以的。
\hskip 5pt
没有括号,\hskip{5pt}
是语法错误
\def\foo{zzz}
定义的标记周围不能有括号,
一些记录为带参数的命令实际上并不带参数,例如,\section
一个不带参数的宏只是向前查找*
或[
在每种情况下调用适当的宏,所以
\section*[short]{l}
可以写成\section*[{short}] l
但不能\section{*}[short]{l}
一些包将^
和重新定义_
为宏并采用如上所述的带有可选括号组的参数,但原始行为很“有趣”,因为 tex 会扩展以寻找可能隐式的括号,例如x^\mathrm x
是x^{\mathrm{x}}
和x^\frac12
是x^{\frac{1}{2}}
作为的第一级扩展\mathrm
并\frac
提供{}
上标所需的组。
答案2
这不是一个完整的答案,但任何使用的东西\@ifnextchar
都可能引起问题。我借用\CheckMinus
了这个答案。
\documentclass{article}
\makeatletter
\def\CheckMinus{\@ifnextchar-{got a minus}{no minus}}
\makeatother
\begin{document}
$\left(1\right)$ % works
%$\left{(}1\right{)}$ % doesn't work
\CheckMinus-
\CheckMinus{-}
\end{document}
答案3
相当多的 TeX“原语”采用了不能在组中分隔的参数。
文本模式和数学模式字距调整命令:
\kern
和\mkern
,每个命令接受 1 个参数。a\kern1em a $a\mkern18mu a$
都可以,但是
a\kern{1em} a
和则$a\mkern{18mu} a$
不行。命令
\raise
和\lower
每个命令接受 2 个参数,其中第一个必须是长度变量,第二个必须是 (TeX) 框。a\raise1ex\hbox{c}
是可以的,但是
a\raise{1ex}\hbox{c}
、a\raise1ex{\hbox{c}}
和a\raise{1ex}{\hbox{c}}
不可以。LaTeX 中
a\raise1ex\hbox{c}
is 的对应\raisebox{1ex}{c}
项也接受两个参数。显然,with\raisebox
是允许的 —— 并且,根据参数的不同,实际上可能是必需的。
答案4
如果宏的最后一个参数由左花括号分隔,则会出现问题。
您可以使用#{
-notation 及其⟨parameter text⟩
定义的 来定义此类宏。
这种宏的一个例子是\name
-macro。
-macro\name
用于避免\expandafter
-orgies \csname..\endcsname
。
-macro\name
处理由左花括号分隔的参数。
其定义如下:
\makeatletter
\@ifdefinable\name{%
\long\def\name#1#{\romannumeral0\innername{#1}}%
}%
\newcommand\innername[2]{%
\expandafter\exchange\expandafter{\csname#2\endcsname}{ #1}%
}%
\newcommand\exchange[2]{#2#1}%
\makeatother
使用方法如下:
\name⟨stuff without curly braces⟩{macroname}
→⟨stuff without curly braces⟩\macroname
例如,
\name{macro}
→ \macro
\name\newcommand{macro}...
→ \newcommand\macro...
\name\global\long\def{macro}...
→ \global\long\def\macro...
\name\string{macro}
→ \string\macro
\name\meaning{macro}
→ \meaning\macro
\name\name\global\let{macroA}={macroB}
→ \name\global\let\macroA={macroB}
→\global\let\macroA=\macroB
假设⟨stuff without curly braces⟩
是一个单一的标记,如下所示:
\name X{Y}
→X\Y
进一步假设您的漂亮打印机将 包裹X
在括号组中。在这种情况下,您有:
\name {X}{Y}
→\X{Y}
,略有不同。
当 TeX 搜索构成带分隔符的宏参数的分隔符的标记时,它不会将嵌套在括号组中的内容作为参数分隔符,除非所讨论的宏嵌套在括号组中。
假设宏的参数由字符分隔a
:
\def\macro#1a{This is the argument: (#1)}
序列
\macro aa
产量:
This is the argument: ()a
假设漂亮打印机将第一个括a
在花括号中:
序列
\macro{a}a
产量:
This is the argument: (a)
,略有不同。