在 `groff eqn` 中使用多个标记和排列对齐方程

Aligning equations with multiple marks and lineups in `groff eqn`

我正在尝试按照我通常在 groff 中使用 LaTeX 的方式对齐一些相当长的方程式。我想要的一般形式:

A = B + C
  = D * E
    + F * G
  = H + I   = J

LaTeX 中,我将按如下方式执行此操作:

\documentclass[fleqn]{article}

\usepackage{amsmath}

\begin{document}
\begin{alignat*}{3}
A
  & = B + C \
  & =
  \begin{aligned}[t]
    & D * E \
    & + F * G
  \end{aligned} \
  & = H + I
    && = J
\end{alignat*}
\end{document}

eqn中,等式对齐是通过marklineup命令完成的。引用 Kernighan 和 Cherry 来自 Typesetting Mathematics 2nd Ed(发现 here)关于这些如何工作:

The word mark may appear once at any place in an equation. It remembers the horizontal position where it appeared. Successive equations can contain one occurence of the word lineup. The place where lineup appears is made to line up with the place marked by the previous mark if at all possible.

读到这里,我的印象是系统并没有禁止用 lineup 与之前的标记对齐,也没有禁止设置新的 mark 在等式的同一行内,例如我期望以下内容:

.PP
.EQ I
A mark =
B + C
.EN
.EQ I
lineup = mark
D * E
.EN
.EQ I
lineup + F * G
.EN

制作这样的东西:

A = B + C
  = D * E
    + F * G

然而,事实并非如此。 eqn 将加号与等号对齐:

A = B + C
  = D * E
  + F * G

并产生警告:

eqn:test.ms:10: multiple marks and lineups

我用一个小脚本编译我的 .ms 文件:

eqn  -Tpdf | groff -ms -Tpdf > ${1/%.ms/.pdf}

我想知道是否有一些宏可以让我存储多个水平偏移(或如何定义一个)。澄清 marklineup 宏的工作原理也会有所帮助。

很遗憾eqn不允许设置新标记。这是一个可能有用的糟糕解决方法。它包括重复第一个等式,但在新位置使用关键字 mark,并将输出转移到无处以使其不出现。 .di 是开始和结束转移的基础 troff。

.EQ I
A mark = B + C
.EN
.EQ I
lineup = D * E
.EN
.di mydump
.EQ I
A = mark B + C
.EN
.di
.EQ I
lineup + F * G
.EN

请注意,您还可以使用 fwd 99 等 eqn 命令向右移动 99/100 em,其中 em 大约是字符“m”的宽度.

这里有一个可能的解决方案,可以让第二个 marklineup 在同一行。它使用基本 troff 序列 \k 将当前水平位置标记到寄存器 myposn 中。然后有一个 eqn 宏 mylineup 调用 eqn special 命令到 运行 一个 troff 定义 MyLineup 其中“returns” 一个具有水平移动的字符myposn 单位。在调用之前,eqn 会用调用后的单词设置一些寄存器,并期望它们被更新为此时所需的任何输出。我们不使用这个虚拟词,它可以是任何东西,但不能省略。例如,

.de MyLineup
. ds 0s "\h'\n[myposn]u'
. nr 0w \n[myposn]u
. nr 0h 0
. nr 0d 0
. nr 0skern 0
. nr 0skew 0
..

.EQ I
define mymark '\k[myposn]'
define mylineup 'special MyLineup'
A mark = B + C
.EN
.EQ I
lineup = mymark D * E
.EN
.EQ I
mylineup dummy + F * G
.EN

.de..的行是MyLineup的定义。两条 define 行应该在第一个等式中。此后,mymarkmylineup dummy可以用来代替marklineup。原厂牌和阵容不变。如有必要,可以在mylineup行中再次使用真正的mark,依此类推。


将其概括为有很多标记是很复杂的。第一个问题是 mymark 只是一个 eqn 定义,不能采用数字参数。它不能使用 special 命令,因为它需要内联。一个简单的解决方案就是在开始时创建很多定义:

define mymark1 '\k[myposn1]'
define mymark2 '\k[myposn2]'

第二个问题是,当eqn调用MyLineup时,它并没有传递以下参数(我们打算将其更改为“1”的“dummy”)作为调用的参数,而是在变量中0s;该字符串也嵌入到格式代码中,实际上(无论如何在我的测试中)\f[R]\,1\/\fP。所以我们需要用 .substring 0s 7 -6 从这个数字中提取数字。修改后的宏和测试为:

.\"   gets passed 0s = \f[R]\,1\/\fP     !!! not just 1
.de MyLineup
. substring 0s 7 -6
. ds 0s \h'\n[myposn\*[0s]]u'
. nr 0w \n[myposn\*[0s]]u
. nr 0h 0
. nr 0d 0
. nr 0skern 0
. nr 0skew 0
..
.EQ I
define mymark1 '\k[myposn1]'
define mymark2 '\k[myposn2]'
define mylineup 'special MyLineup'
A mark = B + C
.EN
.EQ I
lineup = mymark1 D * mymark2 E
.EN
.EQ I
mylineup 1 + F * G
.EN
.EQ I
mylineup 2 + X
.EN
.EQ I
lineup = H + I
.EN

我可能被您的示例中的多个 .EQ 调用误导了。我假设调用和方程式之间会有文本。但是,如果打算有一个单一的多线方程,可以使用 matrix(或者也可能是 lpile)来完成。例如,

.nr PS 20p
.nr VS 24p
.PP
.EQ
matrix { 
 lcol { A }
 lcol { = above = above ~ above = }
 lcol { B + C above D * E above + F * G above H + I }
 lcol { ~ above ~ above ~ above = J }
}
.EN