我对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,如leaf
或node
,但超出此范围的进展证明是困难的。
感谢您的帮助!
答案 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]
您唯一需要改变的是ntreeReplace
和replace
的签名:
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)