Type が定義されています: data Tree = Node Tree Tree | リーフ整数 | 無。メソッドdelete :: Tree -> Int -> Treeを作成して、2 番目のパラメーターで指定された特定の Int を持つすべてのリーフを削除します。
2 に答える
ツリーに特定の構造がない場合は、次のことができます
delete NIL _ = NIL
delete (Leaf i) int | i == int = NIL
| otherwise = Leaf i
delete (Node left right) int = Node (delete left int) (delete right int)
なんで?
delete NIL _ = NIL
端にある空のツリーも含めて、すべてのケースに対処する必要があるためです。は_
、気にしない値を表します。
delete (Leaf i) int | i == int = NIL
| otherwise = Leaf i
| i== int
最初にノードを削除するかどうかを確認する必要があるためです。その場合、それを空の 3 に置き換えNIL
ます。それ以外の場合は、そのままにしておきます。
delete (Node left right) int = Node (delete left int) (delete right int)
ノードにいる場合、とサブツリーのint
両方からを削除する必要があるためです。left
right
あなたはsの全負荷で終わるつもりはありませんNIL
か?
はい、そうなる可能性があると思います。あなたはで片付けることができます
prune (Node NIL NIL ) = NIL
prune (Node (Leaf i) NIL ) = Leaf i
prune (Node NIL (Leaf i)) = Leaf i
prune (Node (Leaf i) (Leaf j)) = Node (Leaf i) (Leaf j)
prune (Node left right ) = prune (Node (prune left) (prune right))
prune t = t
最初の 3 行は左、右、または両方の a を取り除きNIL
、4 行目は 2 つの葉だけを残します。
5 行目は、このノードの左または右のサブツリーのいずれかがノードである場合にのみ呼び出されます。なんでprune
三回?たぶん、あなたprune left
とprune right
それらのうちの1人以上が終わったときNIL
.
1枚ですっきりとした柄合わせprune t = t
もお得です。NIL
Leaf i
AndrewCの回答を改善することをお勧めします。彼の解決策は完全に正しいものですが、潜在的なパフォーマンスの問題があります。
問題は、関数delete
とprune
関数の両方が、呼び出しごとにツリー全体の新しいコピーを作成することです。これは、要素が実際に削除されたかどうかに関係なく発生します。
最悪のシナリオは、存在しない要素を削除することです。
1M の整数を保持する非常に大きなツリーがあるとします。整数はリーフのみに格納されるため、ツリー全体には少なくとも 2M-1 のノードが含まれます。(または、ツリーがまだ剪定されていないため、NILノードが含まれている場合はさらに多くなります)。
このような巨大なツリーから存在しない要素を削除しようとすると、delete
関数は 2M のノードすべてを複製するだけで何もしません。(そして、prune
それらを再び複製します!)
既存の要素を削除することは、ほんの少しだけ良いことです。この場合、delete
1 つのリーフを削除し、その親を更新して、残りのツリーを複製します。prune
おそらくさらにいくつかのノードが削除されますが、残りは複製されます。
なぜそれが起こるのですか?
重複が発生する場所は 2 つあります。
この行は、引数と完全に同一の新しいツリーを作成します。
delete (Leaf i) int | ...
| otherwise = Leaf i
また、この行は、要素が左右の分岐の両方に存在しない場合でも、新しいツリーを作成します。
delete (Node left right) int = Node (delete left int) (delete right int)
不必要な重複を避けることはできますか?
はい、もちろん。引数を変更しない場合は、引数を返すだけです。
これが私のバージョンです:
delete t i = fst $ delete' t i
where delete' NIL _ = (NIL, True)
delete' t@(Leaf i) int | i == int = (NIL, False)
| otherwise = (t, True)
delete' t@(Node left right) int =
let (left', unchangedLeft) = delete' left int
(right', unchangedRight) = delete' right int
in
if unchangedLeft && unchangedRight
then (t, True)
else (Node left' right', False)
ご覧のとおりdelete'
、(Tree, Bool) のペアを返すヘルパー関数を使用します。2 番目の要素は、ツリーが変更されていない場合は True、それ以外の場合は False です。
この関数は、ほとんどのノードを元のツリーと共有する新しいツリーを構築します。ルートから削除された要素へのパス上のノードのみを変更します。
どうprune
ですか?
上記のバージョンはNIL
要素を削除しません。NIL
AndrewC が指摘したように、複数の削除を実行した後、多くのsを持つツリーが作成される場合があります。この問題に対処するprune
には、同様の方法で変更するか、単純に に統合することができますdelete
。
delete t i = fst $ delete'' t i
where delete'' NIL _ = (NIL, True)
delete'' t@(Leaf i) int | i == int = (NIL, False)
| otherwise = (t, True)
delete'' t@(Node left right) int =
let (left', unchangedLeft) = delete'' left int
(right', unchangedRight) = delete'' right int
in
if unchangedLeft && unchangedRight
then (t, True)
else (newNode left' right', False)
newNode NIL r = r
newNode l NIL = l
newNode l r = Node l r