我经常会想定义一些宏来使用非常在整个文档中经常出现 – 例如,我在许多方程式中反复使用的一个特殊符号。
理想情况下,在这种情况下,我希望宏的名称尽可能短。但是,大多数单字母名称似乎已经定义,要么在标准 Latex 类中,要么在常用包中。例如, 、、 、 、 、 、 、\b
和\c
已经\d
定义\i
。\j
通常\l
我\o
只是放弃并使用类似的东西来代替以避免冲突,但它有点冗长。\t
\u
\v
\myX
\X
两个问题:
是否有一个非常短的名字列表可用的并且我可以(合理安全地)将其用于我自己的目的?
是否有一些巧妙的技巧和窍门可用于定义宏的短名称?例如,该
babel
包使"
字符变得特殊,以便您可以使用诸如"=
和之类的序列"a
来输入符号。我是否可以为自己的目的使用类似的技巧,使用另一个字符代替"
- 或者它是唯一尚未分配的字符?
我想坚持使用简单的 7 位 ASCII 字符。
答案1
您可以使用以下命令显示此单字母宏的定义:
$ texdef -t latex a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
该texdef
命令在 CTAN 上可用。
其中大多数似乎都是变音符号。参见https://en.wikibooks.org/wiki/LaTeX/Special_Characters一张桌子。
以下是上述内容的格式化后的输出texdef
:
Undefined:
\e \f \g \h \m \n \p \q \s \w \x \y \z
\A \B \C \D \E \F \G \I \J \K \M \N \Q \R \T \U \V \W \X \Y \Z
\a:
macro:#1->\expandafter \@changed@cmd \csname \string #1\endcsname \relax
\b:
macro:->\OT1-cmd \b \OT1\b
\c:
macro:->\OT1-cmd \c \OT1\c
\d:
macro:->\OT1-cmd \d \OT1\d
\i:
macro:->\OT1-cmd \i \OT1\i
\j:
macro:->\OT1-cmd \j \OT1\j
\k:
macro:->\T1-cmd \k \T1\k
\l:
macro:->\OT1-cmd \l \OT1\l
\o:
macro:->\OT1-cmd \o \OT1\o
\r:
macro:->\OT1-cmd \r \OT1\r
\t:
macro:->\OML-cmd \t \OML\t
\u:
macro:->\OT1-cmd \u \OT1\u
\v:
macro:->\OT1-cmd \v \OT1\v
\H:
macro:->\OT1-cmd \H \OT1\H
\L:
macro:->\OT1-cmd \L \OT1\L
\O:
macro:->\OT1-cmd \O \OT1\O
\P:
macro:->\protect \P
\P :
\long macro:->\ifmmode \mathparagraph \else \textparagraph \fi
\S:
macro:->\protect \S
\S :
\long macro:->\ifmmode \mathsection \else \textsection \fi
答案2
概括。这个答案(我已经在几次迭代中对其进行了扩展)描述了如何采用单个字符(例如"
),使其处于活动状态,并使用它来生成替代的单字符控制序列(例如"a
),其行为与正常控制序列非常相似。
(#1)重新利用典型的非字母字符。如果您没有使用任何"
为其自身目的而重新定义的包,则可以使用它来为宏定义备用命名空间,如下所示。
\makeatletter
\catcode`\"=13
\def"#1{\csname @mymacro#1\endcsname}
\def\newmacro"#1{%
\@ifnextchar [{%
\expandafter\newcommand\csname @mymacro#1\endcsname
}{%
\expandafter\def\csname @mymacro#1\endcsname{%
\csname @@mymacro#1\endcsname\ignorespaces}%
\expandafter\newcommand\csname @@mymacro#1\endcsname
}}
\def\renewmacro"#1{%
\@ifnextchar [{%
\expandafter\renewcommand\csname @mymacro#1\endcsname
}{%
\expandafter\def\csname @mymacro#1\endcsname{%
\csname @@mymacro#1\endcsname\ignorespaces}%
\expandafter\renewcommand\csname @@mymacro#1\endcsname
}}
\makeatother
这样做的目的是"
积极的(解释为单字符控制序列),然后为其提供一个定义,该定义导致"a
或"q
(例如)分别调用控制序列\@mymacroa
和\@mymacroq
。宏\newmacro
和提供了一个简单的接口,\renewmacro
用于定义使用字符调用的宏"
,语法为\newmacro"a{...}
,,\renewmacro"q{...}
ETC。使用类似于\newcommand
和的语法\renewcommand
,包括您想要的任何(可选)参数。我们在定义宏时执行一些额外的测试,以检查它们是否接受参数:如果没有,我们会添加一个带有尾随的额外命令层,以确保宏或(或其他)\ignorespaces
之后的任何空格都被使用。"a
"q
如果您需要与babel
或另一个为 提供特殊含义的包进行互操作"
,则可能需要使用不同的字符 - 您可以自行决定使用哪个字符,尽管它应该是一个带有目录代码 12(或者如果你够大胆,一封通常有目录代码 11如果你的文档语言中有一个几乎从未使用过的字母,例如 w
法语中)。也可以采用多字符解决方案(例如 例如在Christian Lindig 快速切换粗体字体的解决方案)。但是,如果您特别关心击键次数等,多字符解决方案可能会适得其反。
(#2)限制对特殊角色的重新利用。我在评论中注意到,您最感兴趣的是在数学模式下使用这些替代宏。这使我们能够利用进一步的技术。同时,有限的范围使我们可以在为此目的而篡夺的字符上更具冒险精神。
在本例中,我将展示如何使用字符实现与上述类似的效果#
,该字符通常用于枚举宏的参数。我们要做的是使#
活动仅在数学模式中(和显示环境),谨慎的 LaTeX 用户通常可以避免定义新的宏。这还会涉及我们实现界面的方式的一些外观差异。
\makeatletter
\catcode`\#=13
\catcode`\$=6
\def#$1{\csname @mymacro$1\endcsname}
\catcode`\$=3
\catcode`\#=6
\def\newmacro\##1{%
\@ifnextchar [{%
\expandafter\newcommand\csname @mymacro#1\endcsname
}{%
\expandafter\def\csname @mymacro#1\endcsname{%
\csname @@mymacro#1\endcsname\ignorespaces}%
\expandafter\newcommand\csname @@mymacro#1\endcsname
}}
\def\renewmacro\##1{%
\@ifnextchar [{%
\expandafter\renewcommand\csname @mymacro#1\endcsname
}{%
\expandafter\def\csname @mymacro#1\endcsname{%
\csname @@mymacro#1\endcsname\ignorespaces}%
\expandafter\renewcommand\csname @@mymacro#1\endcsname
}}
\makeatother
\everymath=\expandafter{\the\everymath\catcode`\#=13}
\everydisplay=\expandafter{\the\everydisplay\catcode`\#=13}
前几个命令定义了角色#
处于活动状态时执行的操作:IE 与前面的例子相同"
。为此,我们将其#
激活,并暂时将其设置$
为表示宏参数的字符。然后,在继续之前,我们恢复#
and的通常含义$
,只是为了避免麻烦。
人们通常会在序言中定义宏,即事实本身在数学模式之外。因此,我们需要一个略有不同的语法来定义宏,\newmacro
并且\renewmacro
不假设处于#
活动状态(更不用说可能#
需要描述参数!)。最简单的方法是使用语法\newmacro\#a{something}
或\renewmacro\#q[1]{something}
,使用命令序列\#
作为这些命令语法的一部分。请注意,这些命令也可以在文档正文中使用,在正常文本模式下。
最后,我们重新定义了钩子\everymath
和\everydisplay
,每当您使用$ math mode $
或\[ displayed math \]
(包括包提供的环境amsmath
)时都会调用它们,以生成#
活动字符。当离开数学/显示模式时,其作为表示参数的字符的正常状态将恢复。
如果您愿意,请在包含上述代码并使用包的文档中尝试以下内容来试用此代码amsmath
。这旨在演示此代码与 mathmode 以及随后的宏定义的忠实互操作性。
\begin{document}
\newmacro\#a[2]{#1 \stackrel?= #2}
$ #abc $
\begin{align}
\begin{gathered}
#a 1 2 \\
#a {\mathsf{P}}{\mathsf{NP}}
\end{gathered}
& &
#ask
\end{align}
\newcommand\test[2][\bfseries]{\textsf{#1 #2}}
\test{bold and sans-serif}
\test[\itshape]{italic and sans-serif}
\end{document}
(#3) 对这些技术的评论。以这种方式定义的宏的行为与普通控制序列不同,如示例#
所示。具体来说,我在这里描述的机制实际上是为单字符宏名设计的。
"
以我们为了讨论而激活的为例:源代码"abc
将扩展为\@mymacroa bc
而不是\@mymacroabc
。(“宏”的名称在字符的第一个参数处停止"
,在本例中是a
。)如果你尝试使用 定义宏\newmacro"abc{something}
,其效果将与编写相同
\newmacro"a{b}%
c{something}
\@newmacroabc
而不是定义产生 的宏{something}
。
你能定义具有较长名称的宏,例如\newmacro"{abc}
,但这样做很愚蠢。或者,您可以定义"
以尝试获取尽可能多的字母字符(具有 catcode 11)来构成宏的名称,就像 Bruno 在另一个答案中所做的那样。但我对此并不感兴趣,尤其是因为重点是定义替代的单字符宏。这已经是一种非标准机制;如果没有进一步的动机,人们应该在脑海中想出一个特殊情况的具体想法,当人们想要使用它时。我提出了当特殊情况是“访问极短的宏名称”时的解决方案。
答案3
我在一家学术出版商工作多年,我想说的是,在我看来,这样的宏是糟糕的麻烦。你的输入可能与它自己一致,但不会与任何其他人的一致。
但如果你因为一些(对我来说)难以理解的原因而必须定义语法宏,那么迄今为止最好的语法宏是那些实际上描述内容。这些通常不是最短的,但至少当其他人必须编辑您的文本时,输入语法会为生成的输出提供线索。例如:
A \in \Q
\Q
如果是 \mathbb{Q} 则有点合理,但是
\theta \neq \Q
无论 \Q 的定义是什么,我都认为它不好。
使用活动字符而不是反冲的宏绝对是可怕的,原因大致相同:想象一下编辑别人的文件,其中像简单的 @ 符号这样的东西突然变成了宏。
次要参数:在某些工作流程中,您的实际 TeX 输入可以成为结果的一部分(例如,作为 html/epub 视图中位图的替代文本)。没有任何宏定义将保留下来,因此读者(即使是非常了解 LaTeX 的读者)也必须猜测其想要的含义。
答案4
宏\0
,,\1
...,\9
通常被视为用户空间临时 csname:您可以按照自己想要的方式将它们重新用作短宏名。
您可以定义一个宏,使\let
所有这些宏名都等于您在其定义中使用的长 csname,并且如果您担心某些行为不当的包正在重新利用它们,那么您可以使用另一个宏来测试与长 csname 的相等性,并且\ifx
没有被违反。