【问题标题】:Haskell : How do I reverse a the children of a Tree recursiveHaskell:我如何反转树的孩子递归
【发布时间】:2016-07-05 17:46:24
【问题描述】:

我正在尝试在 haskell 中反转 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'))


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 是一个列表。即使我觉得它不应该那么难,我也无法解决这个问题。

【问题讨论】:

  • 首先,你甚至不需要rev (Node v []) = .. case,一般的list case也包括空的list case。其次,使用unzip 似乎是完全合理的——应该消除使用fstsnd(也多次调用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/reverse 版本更有效(而且肯定更丑)。

标签: haskell recursion tree


【解决方案1】:

Zeroth,清理你的代码。你不需要那么多括号,你应该避免重复计算。 fstsnd 是同一件事的两倍?嗯。更好地使用模式匹配:

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' 根本不特定于树列表!它只是一个低效的实现标准reverse函数,它适用于任何列表。

第二,这个解压/压缩有点笨拙。您确定这实际上是您想要的行为吗:现在您只是反转子列表,但 elements 的顺序保持不变。如果没有,那么你最好用一张反向和一张地图来做这一切:

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

【讨论】:

  • 一个挑剔的效率点:出于充分的理由(真的,我保证),reverse 不参与列表融合。因此,理想情况下,reverse . map f 应该手动融合到 mapReverse 的应用程序中(我编的一个名字,很明显)。
  • 一个挑剔的教学点:Arrow 抽象对于像我这样的中级 Haskeller 来说也是令人生畏的。因此,我强烈建议改用来自Data.BifunctorsecondBifunctor 是,IMO,一个对初学者更友好的抽象。
  • 只为(,) 使用Functor 实例怎么样?如果我没记错的话,它和second是一样的。
  • @epsilonhalbe,它实际上是一样的,因为Functor 实例是严格守法的(因此这里可能更好),而Bifunctor 实例只是道德上的所以。所以你可能是对的,fmap 更好。也就是说,(,) 是一个比 (a,) 更直观的 Bifunctor 是一个函子。
  • @dfeuer 我不同意,箭头是 IMO 的出色抽象。 (作为 monad 的替代品,它们很棘手——而且是不必要的,但我不明白 Arrow 类本身有什么难理解的地方——你知道,它只是一个明确的单曲面类别,有什么问题...) Wheras @epsilonhalbe (a,)a-> 的仿函数实例是,嗯...我不喜欢这些。真的,(,) 是双函子的典型例子,而看“这个双函子的一半”是一个相当不平衡的观点。
猜你喜欢
  • 2015-10-02
  • 2022-01-27
  • 2020-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-09
  • 1970-01-01
相关资源
最近更新 更多