替换n-ary树中的元素

时间:2009-11-08 16:43:06

标签: haskell tree

我对Haskell很陌生,但仍然遇到一些问题,让我对脑功能编程感兴趣。随着说:

我有一个自定义的n-ary树数据类型

data Tree = Empty | Leaf String | Node String [Tree]

我正在尝试编写一个函数来替换树中的元素,即

replaceInTree :: String -> String -> Tree -> Maybe Tree

用第二个字符串替换第一个字符串。每个字符串只有一个出现,所以我可以坚持找到第一个字符串。 我已经做了一些努力,但我无法掌握在更换元素后如何重建完整的树。琐碎的,我有这个:

ntreeReplace x y (Node z lis)
    |(x==z) = Just (Node y lis) 

显然只会改变头部node。我编写了一个函数,如果一个元素存在于树中,则返回true,如leafnode,但超出此范围的进展证明是困难的。


感谢您的帮助!

2 个答案:

答案 0 :(得分:2)

这很棘手。如果任何孩子产生匹配,您希望该过程在节点的子节点上短路。这是我的解决方案。

import Data.Maybe

ntreeReplace :: String -> String -> Tree -> Maybe Tree
ntreeReplace x y (Node z lis)
    | (x==z) = Just (Node y lis)
    | otherwise = 
        let (nothings, justs) = span (isNothing . ntreeReplace x y) lis
        in case justs of
             [] -> Nothing
             (t:ts) -> Just (Node z (nothings ++ [fromJust $ ntreeReplace x y t] ++ ts))

ntreeReplace x y (Leaf z)
    | (x==z) = Just (Leaf y)
    | otherwise = Nothing

nTreeReplace如果此树中没有匹配(即,我们应该重新使用输入不变),则返回Nothing,如果进行了替换,则返回Just t(即,我们应该用t替换输入。我使用span将子列表拆分为前缀Nothing和后缀(可能为空),其中第一个元素匹配。

这种实现可能效率很小,因为它会在匹配的子代上调用ntreeReplace两次:一次在span谓词中,一次在构建替换节点时。

我还建议使用更高级别的功能replace,它会返回一个(可能相同的)Tree,而不是Maybe Tree

replace :: String -> String -> Tree -> Tree
replace x y t =
    case ntreeReplace x y t of
      Nothing -> t
      (Just t') -> t'

[编辑]根据@codebliss的建议,您可以将data声明更改为

data Tree a = Empty | Leaf a | Node a [Tree a]

您唯一需要改变的是ntreeReplacereplace的签名:

replace :: Eq a => a -> a -> Tree a -> Tree a
ntreeReplace :: Eq a => a -> a -> Tree a -> Maybe (Tree a)

答案 1 :(得分:0)

这是另一个避免对ntreeReplace进行双重调用的解决方案,代价是构建一堆不必要的列表副本。 (我不知道哪个更有效率。)

我正在使用上面建议的修改后的data定义。

import Data.List

data Tree a = Empty | Leaf a | Node a [Tree a]

-- Returns a tree and a boolean flag indicating whether the tree changed
ntreeReplace :: Eq a => a -> a -> Tree a -> (Bool, Tree a)

ntreeReplace x y t@(Node z ts)
    | (x==z) = (True, Node y ts)
    | otherwise = 
        let (changed,ts') =
                foldl'
                (\(changed,ts') t -> 
                     if changed then (True,t:ts')
                     else
                         let (changed,t') = ntreeReplace x y t
                         in (changed,t':ts'))
                (False,[])
                ts
        in 
          if changed then (True, Node z $ reverse ts')
          else (False,t)

ntreeReplace x y t@(Leaf z)
    | (x==z) = (True, Leaf y)
    | otherwise = (False,t)