如何解决BST:如何根据变形折叠定义“插入”? 拟态
data Tree a
= Empty
| Branch a (Tree a) (Tree a) deriving Show
和变形术
foldt :: b -> (a -> b -> b -> b) -> Tree a -> b
foldt empty _ Empty = empty
foldt empty branch (Branch a l r) = branch a (foldt empty branch l) (foldt empty branch r)
我尝试使用 foldt
定义一个插入函数并得到了一些有趣的结果:
insert :: (Ord a) => a -> Tree a -> Tree a
insert x = foldt (single x) insertb
where insertb a left right
| x == a = Branch x left right
| x < a = Branch a (insert x left) right
| x > a = Branch a left (insert x right)
ghci> mytree = insert 2 (Branch 3 Empty Empty)
ghci> mytree
Branch 3 (Branch 2 (Branch 2 Empty Empty) (Branch 2 Empty Empty)) (Branch 2 Empty Empty)
ghci>
当然,传统的插入方法的行为符合预期:
insert' :: (Ord a) => a -> Tree a -> Tree a
insert' x Empty = single x
insert' x (Branch a left right)
| x == a = Branch x left right
| x < a = Branch a (insert' x left) right
| x > a = Branch a left (insert' x right)
ghci> mytree2 = insert' 2 (Branch 3 Empty Empty)
ghci> mytree2
Branch 3 (Branch 2 Empty Empty) Empty
ghci>
有没有办法根据 insert
来定义 foldt
,还是我在这里挑错了树(ha)?
解决方法
让我们定义一个函数
insertMaybe :: Ord a => Tree a -> Maybe a -> Tree a
这个函数需要一棵树,也可能是一个元素。在 Just
情况下,插入元素。在 Nothing
情况下,树返回不变。那么我们可以定义
insert a t = insertMaybe t (Just a)
现在:
insertMaybe :: Ord a => Tree a -> Maybe a -> Tree a
insertMaybe = foldt leaf branch
where
leaf (Just new) = ?
leaf Nothing = ?
branch a l r Nothing = ?
branch a l r (Just new)
| ... = ?
...
或者:
data Ins a = Ins
{ inserted :: Tree a,notInserted :: Tree a }
insert a t = inserted (insertAndNot a t)
-- Return the tree with the
-- element inserted,and also unchanged.
insertAndNot :: Ord a => a -> Tree a -> Ins a
insertAndNot new = foldt leaf branch
where
leaf = Ins ? ?
branch a ~(Ins li lni) ~(Ins ri rni)
| ... = Ins ? ?
...
拟态
上述解决方案有一个主要的效率问题:他们完全重建树结构只是为了插入一个元素。正如 amalloy 所建议的,我们可以通过将 foldt
(变形)替换为 parat
(变形)来解决这个问题。 parat
允许 branch
函数访问递归修改和未修改的子树。
parat :: b -> (a -> (Tree a,b) -> (Tree a,b) -> b) -> Tree a -> b
parat leaf _branch Empty = leaf
parat leaf branch (Branch a l r) =
branch a
(l,parat leaf branch l)
(r,parat leaf branch r)
方便的是,使用 insert
定义 parat
也稍微更容易。你能看出来吗?这最终成为我建议使用 foldt
的“替代”方式的有效版本。
感谢 dfeuer 和 amalloy 提供的关于拟态的技巧,TIL!
给定 Tree 数据类型的拟态:
parat :: b -> (a -> (Tree a,b) -> b) -> Tree a -> b
parat empty _ Empty = empty
parat empty branch (Branch a l r) =
branch a
(l,parat leaf branch r)
我们可以写一个插入函数:
insert :: Ord a => a -> Tree a -> Tree a
insert x = parat (single x) branch
where branch a (l,l') (r,r')
| x == a = Branch x l r
| x < a = Branch a l' r
| x > a = Branch a l r'
ghci> mytree = insert 2 (Branch 3 Empty Empty)
ghci> mytree
Branch 3 (Branch 2 Empty Empty) Empty
ghci>
测试更大的树...
import Data.Function
mytree :: Tree Integer
mytree = (Branch 3 Empty Empty) & insert 2 & insert 4 & insert 6 & insert 5 & insert 10
inorder :: Tree a -> [a]
inorder = foldt [] (\a l r -> l ++ [a] ++ r)
ghci> mytree
Branch 3 (Branch 2 Empty Empty) (Branch 4 Empty (Branch 6 (Branch 5 Empty Empty) (Branch 10 Empty Empty)))
ghci> inorder mytree
[2,3,4,5,6,10]
ghci>
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。