受到一些启发如何用 LaTeX 自动绘制素数分解树状图?并且主要是因为懒惰,我希望能够为下学期我将担任助教的课程绘制一棵由 LISP 语法定义的树。
对于那些不知道的人来说,LISP 是一种编程语言(事实上是第二古老的语言),它将程序直接指定为树:
(defun count (a l &optional c)
(if l ;; if l is not empty [()===nil]
(count a ;; return the number of a's in the cdr
(cdr l)
(+ (if c c 0) ;; add c (if it wasn't given, start with 0)
(if (equal a (car l)) 1 0))) ;; and 1 or 0, if a==l[0]
c)) ;; otherwise, return the count
给定这个(if ...)
序列,我希望它能得出这样的结果:
\documentclass{article}
\usepackage{tikz}
\usepackage[active,tightpage]{preview}
\PreviewEnvironment{tikzpicture}
\setlength{\PreviewBorder}{10pt}%
\usetikzlibrary{arrows}
\begin{document}
\begin{tikzpicture}[->,>=stealth',level/.style={sibling distance = 10cm/#1,
level distance = 1.5cm}]
\node {if}
child{ node {l} }
child{ node {count}
child{ node {a} }
child{ node {cdr}
child{node {l}}}
child{ node {+}
child{ node {if}
child { node {c} }
child { node {c} }
child { node {0} }}
child{ node {if}
child { node {equal}
child {node {a}}
child {node {car}
child { node {l}}}}
child { node {1}}
child { node {0}}}}}
;
\end{tikzpicture}
\end{document}
显然,风格是最好的,但我希望宏能够避免我的示例所遭受的交叉。
我最好的猜测是以某种方式制作(
和)
激活,但我没有技术经验来立即定义这样的宏。
答案1
以下两种解决方案都可以使用文字&
和
\catcode`\&=12
forest
+xstring
用于在空格处分割事物的概念证明xstring
。不幸的是,设置opening bracket
和closing bracket
对我来说不起作用,因此需要快速搜索和替换。
该afterthough
键被重新定义,以便子项之后的所有内容以及下一个关闭]
都被转发到该splitter*
键。
代码
\documentclass[tikz]{standalone}
\usepackage{forest,xstring}
\forestset{
splitter/.code={%
\fullexpandarg
\IfSubStr{\forestov{content}}{ }{%
\StrCount{\forestove{content}}{ }[\cnt]%
\def\forestPrepend{}%
\foreach \Cnt[evaluate={\CNT=int(\Cnt+1)}] in {\cnt,...,1} {%
\StrBetween[\Cnt,\CNT]{\forestov{content} }{ }{ }[\tmp]%
\xappto\forestPrepend{,prepend={[\expandonce\tmp]}}%
}%
\expandafter\forestset\expandafter{\forestPrepend}%
\StrBefore{\forestov{content}}{ }[\forestContent]%
\forestolet{content}\forestContent%
}{}},
splitter*/.code={%
\noexpandarg
\IfSubStr{ #1}{ }{%
\StrCount{ #1}{ }[\cnt]%
\def\forestAppend{}%
\foreach \Cnt[evaluate={\CNT=int(\Cnt+1)}] in {1,...,\cnt} {%
\StrBetween[\Cnt,\CNT]{ #1 }{ }{ }[\tmp]%
\xappto\forestAppend{,append={[\expandonce\tmp]}}%
}%
\expandafter\forestset\expandafter{\forestAppend}
}{}}}
\makeatother
\begin{document}
\ttfamily
\begin{forest} for tree={delay=splitter, afterthought/.style={for parent={splitter*={#1}}}}
[defun count [a l \&optional c]
[if l
[count a
[cdr l]
[+ [if c c 0]
[if [equal a [car l] ] 1 0]]]
c]]
\end{forest}
\end{document}
输出
forest-qtree
凭借出色的设置forest-qtree.sty
该文件来自 forest 的作者本人,并做了一些小改动(可能在其他地方也需要),这样就更容易了。不幸的是,到目前为止,将括号设置为(
和)
对我来说还没有奏效。
由于某种原因,standalone
在实际树之前输出一个大的空白页,在实际树之后输出一个与树大小相同的页面……
代码
\documentclass[tikz]{standalone}
\usepackage{forest-qtree}
\makeatletter
\forestset{
qtree without dots/.style={% so no empty parent
qtree,
/utils/exec=%
\def\forest@qtree@parse@opening{%
\@ifnextchar.{\forest@qtree@parse@label}{\forest@qtree@parse@label.}%
}}}
\makeatother
\begin{document}
\begin{forest} qtree without dots
[defun count [a l \&optional c]
[if l
[count a
[cdr l]
[+ [if c c 0]
[if [equal a [car l] ] 1 0]]]
c]]
\end{forest}
\end{document}
输出
答案2
这个forest
包是你最好的朋友,因为它允许你指定它用于树的括号!但是,由于它要求所有终端都被括起来,我需要调整 Lisp 代码以满足该要求。我已经很久没用过 Lisp 了,所以我可能把一些东西弄乱了。我还更改了注释字符并转义了&
。更复杂的代码可能会遇到需要转义的其他字符,但这是概念证明。看来你绝对可以使用这种方法,对你的 Lisp 源代码进行最少的更改来生成适当的树。
\documentclass[png,border=4pt]{standalone}
\usepackage{forest}
\bracketset{
opening bracket=(,
closing bracket=)}
\begin{document}
\begin{forest}
(defun (count) (a (l) (\&optional) (c))
(if (l) % (if) (l) (is) (not) (empty) [()===nil]
(count (a) % (return) (the) (number) (of) (a)'s (in) (the) (cdr)
(cdr (l))
(+ (if (c) (c) (0)) % (add) (c) (if (it) (wasn)'t (given), (start) (with) (0))
(if (equal (a) (car (l))) (1) (0)))) % (and) (1) (or) (0), (if) (a)==l[0]
(c))) % otherwise, return the count
\end{forest}
\end{document}