forest 的手册开篇是作者的一段自白:多年来他一直感激地使用 qtree 和 synttree,但市面上的工具都有同一个毛病——树有时会横向铺得太开。正是这一点不满,催生了如今在 LaTeX 中绘制 树形结构(句法树、目录图、决策树、概率树) 的首选工具。不过老资格的 qtree / tikz-qtree 依然健在,而两者的 方括号记法极其相似,却恰恰在一个关键点上不同。不了解这一点就迁移过去,你会在毫无报错的情况下得到一个名字就叫 .S 的节点。本页从这个差异一路讲到如何取舍。
括号记法:一个坐标都不写就能生成树
所有画树的工具都以 括号嵌套 作为输入,原因在于树本身就是递归结构。有一个根,下面挂着子节点,子节点再有孙节点——把这种嵌套直接写成 [ ] 的嵌套,那么「哪个节点放在哪里」的 坐标计算就全部成了工具的活儿。在 forest 中写 [S [NP] [VP]],在 qtree 中写 \Tree [.S [.NP ] [.VP ] ]。你敲进去的不是版面,而是 结构。
实际的选择有三个。建立在 TikZ 与 pgfkeys 之上的 forest(Sašo Živanović;TeX Live 2024 中为 v2.1.5)最灵活,还能自动排布。用最少击键画出语言学句法树的经典工具是 qtree(v3.1b),以及在 TikZ 上重新实现同一记法的 tikz-qtree(David Chiang,v1.2)。而如果只是几层小树、又不想增加宏包,TikZ 自带的 trees 库(child 与 node)就够用。下面先厘清两种括号记法的差别,再逐个来看。
[.S [.NP ]] 与 [S [NP]] 的区别:qtree 的点在 forest 里不管用
先说结论:在 qtree 中,开头的句点是「这是一个分支节点」的标记;而在 forest 中,句点只是一个普通字符。qtree 的 [.S ... ] 表示「名为 S 的非终结节点」,没有句点的裸词则是叶子(终结节点)。forest 没有这套约定,从 [ 之后一直到下一个 [ 或 ] 的全部内容都是标签。因此把 [.S [.NP Kim ]] 交给 forest,会——完全不报错地——生成两个标签分别为 .S 和 .NP Kim 的节点。Kim 不会成为叶子,而是被塞进和 .NP 同一个盒子里。
% qtree / tikz-qtree: a leading dot marks a branching node,
% and a closing ] must be preceded by whitespace
\Tree [.S [.NP Kim ] [.VP [.V saw ] [.NP Lee ] ] ]
% forest: no dots, and every word is a node of its own
\begin{forest}
[S
[NP [Kim]]
[VP [V [saw]] [NP [Lee]]]
]
\end{forest}反方向的错误则失败得响亮得多。qtree 要求闭括号 ] 前必须有空白,所以写成紧凑的 [.NP Kim] 会得到 ! LaTeX Error: \begin{tabular} on input line 5 ended by \end{document}.——文档里一个表格都没有,错误信息却提到 tabular。对 tikz-qtree 做同样的事,则会得到 ! Emergency stop.,连 PDF 都出不来。习惯了 forest 的手在改 qtree 稿件时,第一个绊倒的地方就是这里。总之,两种记法都不会悄悄接受对方,所以在两者之间搬运文件时,请把宏包和记法配成一对来核对。
forest 的自动布局,以及想手动收紧间距时
forest 的核心功能,正是对本页开头那句「树太宽」的回答——自动打包。它按每个节点所支配子树的实际宽度来放置节点,并在不重叠的前提下尽量靠拢,因此即使复杂的树也能直接得到可用的样子。想手动干预时,就对整棵树或某个子树应用 for tree={...}。最常用的三个键是:兄弟之间的水平间距 s sep、层与层之间的垂直距离 l,以及节点内容周围的内边距 inner sep。
\usepackage[linguistics]{forest}
\begin{forest}
for tree={s sep=10mm, inner sep=0, l=0}
[CP
[C]
[IP, for tree={s sep=20mm}
[I, name=src]
[VP
[V]
[DP, roof, name=tgt [the woman]]
]
]
]
\draw[->] (src) to[out=south west, in=south] (tgt);
\end{forest}这个短例几乎囊括了 forest 的全部惯用法。开头的 for tree={...} 会成为整棵树的默认设置;像 [IP, for tree={s sep=20mm} 这样 在节点标签后加逗号,就能只覆盖该子树的设置。给节点加上 name=,就能在树闭合之后写 \draw[->] (src) to[...] (tgt); 来画 表示移位的箭头——因为在 forest 环境内可以直接使用 TikZ 命令。若想让标签本身包含逗号,就像 {NP, PP} 那样 用花括号包住,以免被当成布局指令。如果不想每次都重复同一套外观,可以在导言区用 \forestset{default preamble={for tree={...}}} 定义默认值。
这里有一个最常见的陷阱:在 forest 源码中插入空行会导致解析失败。 空行对 TeX 而言是段落分隔,因此语法分析会在树的中途被切断,报出 ! Paragraph ended before \forestOappto was complete. 这样一个看上去与树毫无关系的错误。想把长树排得好读的心情可以理解,但 能加的只有换行和缩进,空行不行。反过来,缩进深度完全自由,请用它来表现层级。
linguistics 与 edges 库:不加载它们,roof 和 folder 都用不了
forest 的部分功能被拆进了 库 中,必须像 \usepackage[linguistics]{forest} 这样作为选项加载才能使用。最容易踩到的是 roof,它在词组上加一个 三角形「屋顶」。它并非核心 forest 的定义,而属于 linguistics 库,所以在普通的 \usepackage{forest} 下写 [DP, roof [the woman]] 会停在 ! Package pgfkeys Error: I do not know the key '/tikz/roof'。同样地,目录图里常见的 folder 属于 edges 库。
顺便弄清 linguistics 库到底做了什么,可以少走弯路。第一是 sn edges:forest 的默认做法是把父子节点的「边界」相连,而这个设置改为 从父节点下缘(south)连到子节点上缘(north)——语言学句法树里看惯的正是这种形状。第二是 把基线对齐到根节点,正因如此,树才能与例句编号或释义行的高度对齐。第三是定义了 与 c 统制(c-command)相关的节点遍历,用 c-commanded 和 c-commanders 可以一次性指定处于该关系的整组节点。至于 edges 库,除了 folder 之外,还提供把枝干折成直角的 forked edge / forked edges。
qtree 与 tikz-qtree:一行写出句法树
若只是快速画一棵句法树,\Tree 记法至今仍是最短路径。载入 \usepackage{qtree},正文里写一行 \Tree [.S [.NP Kim ] [.VP [.V saw ] [.NP Lee ] ] ] 就能得到一棵树。它内部是两个人工作的汇合:绘树的宏本体是 Jeffrey Mark Siskind 的 QobiTree,而让它在 LaTeX 中用起来顺手的前端和扩展则出自 Alexis Dimitriadis 之手。自 3.0b 版(2005 年)起改用 pict2e 画线,因此无论走 DVI/PostScript 路线还是直接输出 PDF,结果都一致。
qtree 内置了一整套专为句法树设计的小技巧。在树中,NP_i 和 N^0 会 自动整理成数学模式的上下标,X\1 则是 X$'$(X′)的缩写。要在词组上加三角形,写成 \qroof{out of style}.PP(与 forest 的 roof 作用相同,只是拼法不同)。树默认居中,若想左对齐就用 \usepackage[nocenter]{qtree}。
tikz-qtree(David Chiang)把同样的 \Tree 记法 在 TikZ 之上重建。同时载入 \usepackage{tikz} 与 \usepackage{tikz-qtree},原来的源码就能原样通过。作者本人列出的好处有三点:排布节点时能避免碰撞(不同于 TikZ 标准的树)、为 qtree 补上 TikZ 的功能(枝的标签、节点之间的箭头),以及不同于 pst-qtree,它 可用于 pdfTeX 和 XeTeX。树的方向也可以调整:给 tikzpicture 传入 [grow'=right],树就会 从左向右 生长。兼容性虽高但并非百分之百,因此迁移复杂的树时请编译一遍并用眼睛确认。
\usepackage{tikz}
\usepackage{tikz-qtree}
% sideways tree: grow to the right instead of downwards
\begin{tikzpicture}[grow'=right]
\Tree [.S [.NP Kim ] [.VP [.V saw ] [.NP Lee ] ] ]
\end{tikzpicture}非语言学的树:目录结构、决策树、概率树
forest 并不是语言学专用工具。要画 目录结构图,标准搭配是 edges 库的 folder 加上 grow'=east,用folder 式的直角分枝从左向右铺开。要画 决策树或概率树,则希望把数值直接标在枝上——由于 forest 的枝就是 TikZ 的路径,用 edge label={node[midway,above]{0.3}} 挂上一个节点,就能把概率或条件直接写在边上。forest 附带的手册里也收录了决策树的示例。
\usepackage[edges]{forest}
% a file hierarchy: right-angled branches, growing to the right
\begin{forest}
for tree={grow'=east, folder, s sep=1mm}
[project
[src [main.tex] [chapters]]
[figures [plot.pdf]]
]
\end{forest}
% a probability tree: labels ride on the edges
\begin{forest}
for tree={grow'=east, l sep=18mm, s sep=6mm, anchor=west}
[start
[rain, edge label={node[midway,above,font=\scriptsize]{0.3}}]
[dry, edge label={node[midway,below,font=\scriptsize]{0.7}}]
]
\end{forest}如果只是几层小树、又完全不想增加宏包,TikZ 自带的 trees 库 就够了。放好一个节点后接上 child {...},其中的内容会作为子节点排布,并自动画出从父到子的边。child 内通常写 node {...},再继续嵌套 child 生成孙节点。布局由两个距离决定:父层与子层之间的垂直距离 level distance,以及并排兄弟之间的间距 sibling distance——两者默认都是 15 mm。要按层调整就用 level ⟨n⟩/.style,例如 level 1/.style={sibling distance=4cm};树的方向可用 [grow=right] 之类改变,枝的形状由 edge from parent 控制(直角连接用 edge from parent fork down)。
\usetikzlibrary{trees}
\begin{tikzpicture}[level distance=12mm, sibling distance=24mm]
\node {root}
child { node {a} }
child { node {b}
child { node {c} }
child { node {d} }
}; % one semicolon for the whole tree
\end{tikzpicture}书写时要注意:整棵树只有一个分号 ;,放在 \node ... ; 的末尾。而且原生 TikZ 没有 forest 那样的自动打包,所以子节点数量或深度增加后,你就得手动加大 sibling distance 来避免重叠。几层的小树这样足够了,但当你开始希望「间距能随内容自动调整」时,那就是该转向 forest 的信号。
该选哪一个
拿不准时,新画的树就用 forest。它有自动布局,覆盖从语言学到一般图示的广泛用途,装饰上可以直接使用 TikZ 的全部能力。如果要接手已有的 \Tree 稿件,或合作者习惯用 \Tree 写,就沿用 qtree / tikz-qtree——二者之中,tikz-qtree 输出更好、也更容易调整。完全不想增加依赖的几层小树,用原生 TikZ 的 child 即可。四者都在 CTAN 上;forest 和 tikz-qtree 位于 graphics/pgf/contrib/ 下,依赖 TikZ/pgf。细节方面,在本机运行 texdoc forest、texdoc qtree 或 texdoc tikz-qtree 最为可靠。
| 工具 | 记法 | 适合用途 |
|---|---|---|
forest | [S [NP] [VP]](不加句点) | 一般的新建树;自动打包,从语言学到决策树 |
qtree | \Tree [.S [.NP Kim ] ](句点和空格必需) | 最短的句法树;基于 pict2e,轻量 |
tikz-qtree | \Tree(与 qtree 相同) | 同样记法但达 TikZ 品质;还能加枝标签和箭头 |
tikz trees library | \node {r} child {node {a}}; | 几层的小树;不想增加额外宏包时 |
最后说一点实务。想把句法树放进会议幻灯片或讲课课件的需求非常多,这时与其让树留在 LaTeX 文档里,不如 导出为独立图像 再贴进 PowerPoint 或 Keynote,往往更快。由于 forest 和 tikz-qtree 都建立在 TikZ 之上,用 standalone 类包一层,就能直接得到裁剪紧凑的 PDF 或 SVG。具体步骤在图像导出那一页里有整理。