【问题标题】:Rewriting trees in haskell在haskell中重写树
【发布时间】:2013-10-24 06:21:08
【问题描述】:

一般情况:
我想知道如何写入树(即更改底层的特定节点,将其替换为具有不同值的节点,该节点将旧节点作为左子节点,将新节点作为右子节点)


使其变得更加困难的特定应用程序:
我正在尝试编写一个类似于 20 个问题的游戏,该游戏从文件中读取现有树,向用户询问各种问题,如果不知道答案,则会向用户询问最终猜测和最终猜测之间的区别问题正确答案以及正确答案,并将新条目添加到游戏中(将猜测所在的位置替换为指向猜测和答案的节点中的新问题)

【问题讨论】:

  • 到目前为止你有什么尝试吗?你的树形结构是什么样的?
  • 是的,(我现在可以使用它,只是效率极低)我最终使用的替换功能将遍历整个树并查找具有给定输入值的节点,然后替换这些节点与另一个树结构树看起来像这样: data Tree a = Empty |节点a(Tree a) (Tree a) 推导(Show,Read,Eq)

标签: haskell tree binary-tree


【解决方案1】:

Monad 结构和这种树嫁接之间通常存在紧密的对应关系。这是一个例子

data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving Functor

instance Monad Tree where
  return = Leaf
  Leaf a >>= f = f a
  Branch l r >>= f = Branch (l >>= f) (r >>= f)

(>>=) 只是基于某些功能 f :: a -> Tree a 进行叶子扩展(树嫁接)。

然后我们就可以轻松进行您要寻找的嫁接

graftRight :: Eq a => a -> a -> Tree a -> Tree a
graftRight a new t = t >>= go where
  go a' | a' == a   = Node a new
        | otherwise = Leaf a'

但这非常低效,因为它会访问您树中的每个Leaf,以搜索您要替换的特定那个。如果我们知道更多信息,我们可以做得更好。如果树以某种方式排序和排序,那么您可以使用fingertreesplaytree 进行有效替换。如果我们只知道要通过其路径替换的节点,我们可以使用 Zipper。

data TreeDir = L | R
data ZTree a = Root 
             | Step TreeDir (Tree a) (ZTree a)

这让我们可以进出树的根

stepIn :: Tree a -> (Tree a, ZTree a)
stepIn t = (t, Root)

stepOut :: (Tree a, ZTree a) -> Maybe (Tree a)
stepOut (t, Root) = Just t
stepOut _         = Nothing

一旦我们进去了,就往我们喜欢的任何方向走

left :: (Tree a, ZTree a) -> Maybe (Tree a, ZTree a)
left (Leaf a, zip) = Nothing
left (Branch l r, zip) = Just (l, Step R r zip)

right :: (Tree a, ZTree a) -> Maybe (Tree a, ZTree a)
right (Leaf a, zip) = Nothing
right (Branch l r, zip) = Just (r, Step L l zip)

up :: (Tree a, ZTree a) -> Maybe (Tree a, ZTree a)
up (tree, Root) = Nothing
up (tree, Step L l zip) = Just (branch l tree, zip)
up (tree, Step R r zip) = Just (branch tree r, zip)

编辑树叶

graft :: (a -> Tree a) -> (Tree a, ZTree a) -> Maybe (Tree a, ZTree a)
graft f (Leaf a, zip) = Just (f a, zip)
graft _ _             = Nothing

或者也许使用我们从上方绑定的某个位置下方的所有叶子!

graftAll :: (a -> Tree a) -> (Tree a, ZTree a) -> (Tree a, ZTree a)
graftAll f (tree, zip) = (tree >>= f, zip)

然后我们可以在进行移植之前走到树上的任何一点

graftBelow :: (a -> Tree a) -> [TreeDir] -> Tree a -> Maybe (Tree a)
graftBelow f steps t = perform (stepIn t) >>= stepOut where
  perform =     foldr (>=>) Just (map stepOf steps)          -- walk all the way down the path
            >=> (Just . graftAll f)                      -- graft here
            >=> foldr (>=>) Just (map (const up) steps)      -- walk back up it
  stepOf L = left
  stepOf R = right

>>> let z = Branch (Branch (Leaf "hello") (Leaf "goodbye"))
                   (Branch (Branch (Leaf "burrito")
                                   (Leaf "falcon"))
                           (Branch (Leaf "taco")
                                   (Leaf "pigeon")))

>>> graftBelow Just [] z == z
True

>>> let dup a = Branch (Leaf a) (Leaf a)
>>> graftBelow dup [L, R] z
Just (Branch (Branch (Leaf "hello") 
                     (Branch (Leaf "goodbye") 
                             (Leaf "goodbye"))) 
             (Branch (Branch (Leaf "burrito") (Leaf "falcon")) 
                     (Branch (Leaf "taco") (Leaf "pigeon"))))

>>> graftBelow dup [L, R, R] z
Nothing

通常有很多方法可以实现这一目标。

【讨论】:

  • 谢谢!这比我最终编写的方式更有效(它会搜索每一片叶子,而不是记录所采取的步骤)
猜你喜欢
  • 1970-01-01
  • 2014-04-30
  • 1970-01-01
  • 1970-01-01
  • 2015-08-29
  • 2023-03-05
  • 2012-06-07
  • 2014-11-01
  • 2020-02-05
相关资源
最近更新 更多