forest+xstring

forest+xstring

受到一些启发如何用 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 bracketclosing 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}

代码输出

相关内容