闲来无事,细读了一下 amsmath.sty 中关于这类自适应长度的箭头(即 $\vv{XXX}$、$A\xrightarrow[XX]{XXX}B$ 这类)的代码,可知它们都是基于一条命令:\arrowfill@ ,其定义如下:
\def\arrowfill@#1#2#3#4{%
$\m@th\thickmuskip0mu\medmuskip\thickmuskip\thinmuskip\thickmuskip
\relax#4#1\mkern-7mu%
\cleaders\hbox{$#4\mkern-2mu#2\mkern-2mu$}\hfill
\mkern-7mu#3$%
}
关键之处是 \cleaders\hbox{...}\hfill 这行,它将重复 \hbox{...} 来填充给定的空间,这句的前后还各有一个东西,所有东西之间都有负距离,具体来说,\arrowfill@{左}{中}{右}#4(这个#4将会放入style,这里不用管它)得到的是:
左 -9mu 中 -4mu 中 -4mu 中 ... 中 -9mu 右
为什么两边的负距离更大?是因为总长由实际内容而定,需填充的长度一般不为 \hbox{...} 的整数倍,所以两边会余留一点小空白(长度不确定但有上界),因此需要更大的负距离才能确保线不断开。
之后定义向右的可伸长箭头时是这样的:
\def\rightarrowfill@{\arrowfill@\relbar\relbar\rightarrow}
其中 \relbar 是一条短线,因此,可伸长箭头实际上是由短箭头与若干条短线连接起来的,并且连接处是略有重叠的,这就解释了为什么有时看PDF上的长箭头会觉得线的粗浅不均(从楼上上的图也能看出来),当然这跟阅读器有关,打印出来应该不会有这问题。
明白了原理之后,就知道上面用 $\triangleright$ 来做的箭头是有问题的,上面那些测试之所以看起来没问题,完全是运气好,一旦做更细致的测试就会发现不妥:
接下来看看向量箭头和 $A\xrightarrow[XX]{XXX}B$(\xrightarrow)这种箭头的定义。
前者是用一个叫 \overarrow@ 的命令把箭头做上去的,不复杂,暂时懒得讲了,扯扯后者,它里头有一个命令叫 \ext@arrow ,其定义如下:
\def\ext@arrow#1#2#3#4#5#6#7{%
\mathrel{\mathop{%
\setbox\z@\hbox{#5\displaystyle}%
\setbox\tw@\vbox{\m@th
\hbox{$\scriptstyle\mkern#3mu{#6}\mkern#4mu$}%
\hbox{$\scriptstyle\mkern#3mu{#7}\mkern#4mu$}%
\copy\z@
}%
\hbox to\wd\tw@{\unhbox\z@}}%
\limits
\@ifnotempty{#7}{^{\if0#1\else\mkern#1mu\fi
#7\if0#2\else\mkern#2mu\fi}}%
\@ifnotempty{#6}{_{\if0#1\else\mkern#1mu\fi
#6\if0#2\else\mkern#2mu\fi}}}%
}
随后定义箭头为:
\newcommand{\xrightarrow}[2][]{\ext@arrow 0359\rightarrowfill@{#1}{#2}}
\newcommand{\xleftarrow}[2][]{\ext@arrow 3095\leftarrowfill@{#1}{#2}}
参数真多,#5 是相应的箭头,#6、#7 是箭头下上方的文字,至于前四个数字,不难看出,#1、#2 是控制上下方文字的左右偏移的,设计者大概觉得,对于右箭头,文字稍微向左偏会好看些,所以先 0 后 3,左箭头则反过来先 3 后 0。
而令我不解的是 #3、#4,对于 \setbox\tw@ 那部分,如果我没理解错,是要获取箭头应有的长度,它将比上下方文字的最大宽度大 (#3+#4)mu(\scriptstyle下),那么 0359 和 030{14} 甚至 03{-100}{114} 应该都是同样的效果,实测发现的确如此,既然只与和有关,何必要两个参数?
明白了原理之后,也可以自己构造这类箭头了,比如说,现在有向左和向右,却没有双向,于是我们可以补充一个:
\newcommand{\xleftrightarrow}[2][]{\ext@arrow 000{17}\leftrightarrowfill@{#1}{#2}}
(为什么我会设成 000{17} ?)
再比如,现在有单线,却没有双线,注意楼上所讲的,\Leftarrowfill@ 等已有定义,只是没被用过,现在正好可以用上(amsmath故意的?):
\newcommand{\xRightarrow}[2][]{\ext@arrow 0359\Rightarrowfill@{#1}{#2}}
\newcommand{\xLeftarrow}[2][]{\ext@arrow 3095\Leftarrowfill@{#1}{#2}}
\newcommand{\xLeftrightarrow}[2][]{\ext@arrow 000{17}\Leftrightarrowfill@{#1}{#2}}
这样,左右双向单线双线共六个箭头,效果如下:
其实,这类箭头已经有宏包统一做过了,它名叫 extarrows ,打开看里面的代码,和上面的差不多,只是都没偏移,全是 0099。 |