ソースへのアプリケーションのリンクを使用して、Data.Tree
私はこれを思いついた。私はそれについてもっと学ぶことができるように自分で書きたかったのです。ソースのdrawTree
メソッドは、複数の子を持つノードで機能するように一般化されています。私は二分木のためだけです。
注:私のツリー定義は、OPのものとは少し異なります。typeパラメーターが何に使用されるのかよくわかりませんa
が、アプローチは同じである必要があります
data Tree a
= Branch (Tree a) a (Tree a)
| Leaf
prettyprint (Leaf)
= "Empty root."
-- unlines concats a list with newlines
prettyprint (Branch left node right) = unlines (prettyprint_helper (Branch left node right n h))
prettyprint_helper (Branch left node right)
= (show node) : (prettyprint_subtree left right)
where
prettyprint_subtree left right =
((pad "+- " "| ") (prettyprint_helper right))
++ ((pad "`- " " ") (prettyprint_helper left))
pad first rest = zipWith (++) (first : repeat rest)
prettyprint_helper (Leaf)
= []
のような木を生成します
4
+- 8
| +- 9
| | +- 10
| `- 6
| +- 7
| `- 5
`- 2
+- 3
`- 1
pad
関数がどのように機能するかを説明したかったのですが、それは私が理解するのが最も困難だったためです(shift
ソースで呼び出されています)。
まず、zipWith
関数(最初の引数)を適用して、2つのリストを「結合」します。zipWith (+) [1, 2, 3] [4, 5, 6]
戻り[5, 7, 9]
ます。リストの1つが空になると停止します。zipWith
1つのリストにのみ適用すると、2番目のリストをzip形式で圧縮するために適用できる関数が返されます(これは関数カリー化として知られていると思います)。pad
関数のより単純なバージョンは次のとおりです。
> let pad = zipWith (++) (repeat " ")
> :type pad
pad :: [[Char]] -> [[Char]]
> pad ["1", "2", "3"]
[" 1", " 2", " 3"]
注意:1。リストの1つは無限大(repeat " "
)ですが、リストの1つが空になると圧縮が停止します。2。zipWith
関数とリストのみを取ります。pad
次に、文字/文字列のリストのリストを取得し、文字/文字列のリストのzipリストを返す関数です。したがってpad
、1つのリストに適用して、最初のリストで圧縮します。
それでは見てみましょう
prettyprint_subtree left right =
((pad "+- " "| ") (prettyprint_helper left))
++ ((pad "`- " " ") (prettyprint_helper right))
(pad "+- " "| ")
のような無限のリストを作成します["+- ", "| ", "| ", "| ", ...]
。(prettyprint_helper right)
右側のルートノードから始めて、右側のサブツリーを表す行のリストを作成します。ただし、そのツリー全体を右にシフトする必要があります。パディングでそれを圧縮することによってそれを行います。サブツリーの大きさがわからないため、無限リストを使用します。"| "
余分な行を埋めるのに十分なsが常にあります(これは遅延評価のためにも機能します)。最初の行に注意してください。つまり、subtree-root-nodeには、"+- "
代わりに、右側のノードの「表記」が埋め込まれます。
左側はほぼ同じです。左側のノードの表記はです"`- "
。他の唯一の違いはパディングです。" "
の代わりに"| "
。では、なぜノードを離れるのに「ブランチ」が必要ないのでしょうか。さて、あなたはそれを右のノードの後ろ(パディングが追加されています;左側にあります)が下の左のノードに行くと考えることができます。左側のノード/サブツリーを親ノードに接続するには、右側の後ろにパディングが必要です。おそらく親ツリーを除いて、ツリーの左側の後ろには何もありません。これが私の最後のポイントになります。関数内の行のリストとして表されるすべてのサブツリーは、prettyprint_helper
すべての親ツリーを上にパディングする追加レベルを取得します。例を挙げて説明するのが一番いいと思います。
上記のツリーを作成する際(特に遅延評価では、実行順序が正確にはわかりませんが、これは、なぜそれが機能するのかを視覚化するのに役立つだけです):
に再帰するとし10
ます。左側のサブツリーと右側のサブツリーは空なので、をprettyprint_helper (Branch Leaf 10 Leaf)
返します["10"]
。
今、私たちはまでです9
。サブツリーは次のとおりです:("9" : ([] ++ ((pad "+- " "| ") [10]))
左側なし)、または"9" : ["+- 10"]
、または:
9
+- 10
今、私たちはまでです8
。((pad "+- " "| ") (prettyprint_helper right))
作成:
+- 9
| +- 10
自分でトレースできますが、左側は次のとおりです。
6
+- 7
`- 5
どのパッドに(最初の要素"`- "
、残り" "
):
`- 6
+- 7
`- 5
したがって、左側が右側に追加された8の場合、次のようになります。
8
+- 9
| +- 10
`- 6
+- 7
`- 5
1つ上のステップに進むと、この8
サブツリーに4
ツリーが埋め込まれ、反対側をトレースして機能することを確認できます。あなたが得る
+- 8
| +- 9
| | +- 10
| `- 6
| +- 7
| `- 5
そして、最終的な結果は上記のとおりです。このプロセス全体を通して、ツリーは行のリストとして表されることを忘れないでください。最後にのみ、と一緒になりunlines
ます。サブツリーが複数行の文字列として渡されているように見える可能性があるため、おそらく私の描画は誤解を招く可能性があります。これを理解すると、の関数の"|"
ように、左右のノードの間に余分なブランチ()を追加するのは非常に簡単です。私はあなたにそれを理解させます:)Data.Tree
drawTree
これが過剰な場合はお詫び申し上げます。初心者としてソースから理解するのは非常に難しく、これは私にとって大きなジャンプでした。この問題を解決しようとしている他の誰かに役立つことを願っています。