Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/jpa/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 哈斯凯尔:我如何反转递归树的子树_Haskell_Recursion_Tree - Fatal编程技术网

Haskell 哈斯凯尔:我如何反转递归树的子树

Haskell 哈斯凯尔:我如何反转递归树的子树,haskell,recursion,tree,Haskell,Recursion,Tree,我正试图扭转哈斯克尔一棵树上的孩子们。这棵树看起来像这样: data Tree v e = Node v [(e,Tree v e)] 目前,我使用以下两个功能: rev :: Tree v e -> Tree v e rev (Node v []) = Node v [] rev (Node v xs) = let xs' = (rev'(snd (unzip xs))) in Node v (zip (fst (unzip xs)) (map rev xs')) re

我正试图扭转哈斯克尔一棵树上的孩子们。这棵树看起来像这样:

data Tree v e = Node v [(e,Tree v e)]  
目前,我使用以下两个功能:

rev :: Tree v e -> Tree v e
rev (Node v []) = Node v []
rev (Node v xs) =
  let xs' = (rev'(snd (unzip xs)))
  in Node v (zip (fst (unzip xs)) (map rev xs'))


rev' :: [Tree v e] -> [Tree v e]
rev' [] = []
rev' (x:xs) = (rev' xs) ++ [x]  
我甚至不确定这是否100%有效。有没有什么方法可以在一个函数中实现递归呢?我觉得我的方式效率很低。我试图编写一个如下所示的函数:

revRec :: Tree v e -> Tree v e
revRec (Node v [])     = Node v []
revRec (Node v (x:xs)) = Node v (revRec xs ++ [x]) 

显然,我不能用
xs
调用
revRec
,因为
xs
是一个列表。尽管我觉得这个问题不应该那么难,但我还是不能把我的头脑集中在这个问题上。

Zeroth,清理你的代码。你不需要那么多参数,你应该避免重复计算<代码>fst和
snd
在同一件事上两次?无聊的。更好地使用模式匹配:

rev :: Tree v e -> Tree v e
rev (Node v xs)
  = let (es, cs) = unzip xs
        xs' = rev' cs
    in Node v $ zip es (map rev xs')

rev' :: [Tree v e] -> [Tree v e]
rev' [] = []
rev' (x:xs) = rev' xs ++ [x]  
首先请注意,
rev'
根本不是特定于树列表的!这只是标准函数的一个低效实现,它适用于任何列表

第二,这个解压有点笨拙。你确定这就是你想要的行为吗:现在你只是在反转子元素列表,但是元素的顺序保持不变。如果没有,则最好使用一个反转和一张地图:

rev :: Tree v e -> Tree v e
rev (Node v xs) = Node v . reverse $ map (\(e,c) -> (e, rev c)) xs
或者,甚至更好

import Control.Arrow

rev (Node v xs) = Node v . reverse $ map (second rev) xs

Zeroth,清理代码。你不需要那么多参数,你应该避免重复计算<代码>fst和
snd
在同一件事上两次?无聊的。更好地使用模式匹配:

rev :: Tree v e -> Tree v e
rev (Node v xs)
  = let (es, cs) = unzip xs
        xs' = rev' cs
    in Node v $ zip es (map rev xs')

rev' :: [Tree v e] -> [Tree v e]
rev' [] = []
rev' (x:xs) = rev' xs ++ [x]  
首先请注意,
rev'
根本不是特定于树列表的!这只是标准函数的一个低效实现,它适用于任何列表

第二,这个解压有点笨拙。你确定这就是你想要的行为吗:现在你只是在反转子元素列表,但是元素的顺序保持不变。如果没有,则最好使用一个反转和一张地图:

rev :: Tree v e -> Tree v e
rev (Node v xs) = Node v . reverse $ map (\(e,c) -> (e, rev c)) xs
或者,甚至更好

import Control.Arrow

rev (Node v xs) = Node v . reverse $ map (second rev) xs

首先,您甚至不需要
rev(Node v[])=案例,常规列表案例也包括空列表案例。其次,使用
unzip
似乎非常合理-可能应该取消使用
fst
snd
(也可以多次调用
unzip
)。您可以使用原语递归编写它,例如:
rev(Node a ts)=Node a$uncurry(\x y->zip x(y[]))$foldr(\(e,t)(as,bs)->(e:as,bs.(rev t:))([],id)ts
。但我真的怀疑这是否比
解压/反转版本更有效(当然更难看)案例,常规列表案例也包括空列表案例。其次,使用
unzip
似乎非常合理-可能应该取消使用
fst
snd
(也可以多次调用
unzip
)。您可以使用原语递归编写它,例如:
rev(Node a ts)=Node a$uncurry(\x y->zip x(y[]))$foldr(\(e,t)(as,bs)->(e:as,bs.(rev t:))([],id)ts
。但我真的怀疑这是否比
解压/反转版本更有效(而且肯定更难看)。一个挑剔的效率点:出于充分的理由(真的,我保证),
反转
不参与列表融合。因此,
相反。map f
理想情况下应该手工融合到
mapReverse
的应用程序中(这是我编的一个名字,意义很明显)。一个挑剔的说教要点是:
箭头
抽象对我这样的中级Haskeller来说都是可怕的。因此,我强烈建议使用
Data.Bifunctor
中的
第二个
<在我看来,code>Bifunctor
是一个对初学者更为友好的抽象概念。对于
(,)
,只使用Functor实例怎么样?如果我没记错的话,它与第二个相同。@epsilonhalbe,它实际上不一样,因为
函子
实例是严格守法的(因此在这里可能更好),而
双函子
实例只是道德上如此。所以你可能是对的,
fmap
更好。这就是说,
(,)
(a,)
是一个函子更直观的
双functor
。@dfeuer我不同意,箭头是一个很好的抽象。(作为单子的替代品,它们很棘手——而且不必要,但我不认为
Arrow
类本身有什么难以理解的地方——你知道,它只是一个很好的单项式范畴,有什么问题……)Wheras@epsilonhalbe是
的函子实例(a,)
a->
都是,嗯……我不是这些的粉丝。真的,
(,)
是一个典型的双功能的例子,看“一半双功能”是一个非常片面的观点。一个挑剔的效率点:有充分的理由(真的,我保证),
reverse
不参与列表融合。因此,理想情况下,
reverse.map f
应该手工融合到
mapReverse
的应用程序中(一个我自己编的名字,意义很明显)一个很挑剔的说教点:
Arrow
抽象对像我这样的中级Haskeller来说都是很吓人的。因此,我强烈建议使用
Data.Bifunctor
中的
second
Bifunctor
在我看来是一个更为初学者友好的抽象或者
(,)
?如果我没记错的话,它与
是一样的。@epsilonhalbe,它实际上不一样,因为
函子
实例严格遵守法律(因此这里可能更好)而
Bifunctor
的例子只是在道德上如此。所以你可能是对的,
fmap
更好。也就是说,
(,)
bein是有理由的