【问题标题】:Generalized foldr and foldl for using with generic Haskell trees?用于通用 Haskell 树的通用 foldr 和 foldl?
【发布时间】:2011-02-13 20:56:25
【问题描述】:

如何为通用 Haskell 树编写通用的 foldr 和 foldl 函数?

data (Eq a, Show a) => Tree a = Void | Node a [Tree a]
    deriving (Eq, Show)

treefoldr :: (Eq a, Show a) => 
   (a -> b -> c) -> c -> (c -> b -> b) -> b -> Tree a -> c

treefoldl :: (Eq a, Show a) =>
   (b -> a -> c) -> c -> (c -> b -> b) -> b -> Tree a -> c

即使我能理解 foldr 和 foldl 函数在 Haskell 中的工作原理,我也不太清楚如何为树编写这个通用函数。

编辑:我尝试过这样的事情(甚至没有编译):

treefoldr  _ g1 _ _    Void       = g1
treefoldr f1 g1 f2 g2 (Node a ts) = f1 a (foldr f2 g2 ts)

EDIT 2:再试一次...

treefoldr _ z1 _ _   Void      = z1
treefoldr f z1 g z2 (Node a ts) =
   f a (foldr g z2 (map (\x -> treefoldr f z1 g z2 x) ts))

treefoldl _ z1 _ _   Void      = z1
treefoldl f z1 g z2 (Node a ts) =
   f (foldl g z2 (map (\x -> treefoldl f z1 g z2 x) ts)) a

treefoldr 工作正常,但 treefoldl 不工作:

Couldn't match expected type `c' against inferred type `b'
      `c' is a rigid type variable bound by
          the type signature for `treefoldl' at trees.hs:47:42
      `b' is a rigid type variable bound by
          the type signature for `treefoldl' at trees.hs:47:32
    In the first argument of `foldl', namely `g'
    In the first argument of `f', namely
        `(foldl g z2 (map (\ x -> treefoldl f z1 g z2 x) ts))'
    In the expression:
        f (foldl g z2 (map (\ x -> treefoldl f z1 g z2 x) ts)) a

【问题讨论】:

  • 你说它没有编译。你明白错误信息了吗?
  • @Dave Hinton: Couldn't match expected type c against inferred type Tree a,我认为treefoldr 必须返回一个类型c,而它返回的是一个完整的Tree a
  • 错误信息是否也说In the third argument of `foldr', namely `ts'
  • 顺便说一句,去掉 Tree 上的类型类上下文。它们没用,因为你仍然需要到处重复它们。

标签: haskell tree fold


【解决方案1】:

完整的错误信息:

Couldn't match expected type `c' against inferred type `Tree a'
  `c' is a rigid type variable bound by
      the type signature for `treefoldr' at so.hs:5:14
  Expected type: [c]
  Inferred type: [Tree a]
In the third argument of `foldr', namely `ts'
In the second argument of `f1', namely `(foldr f2 g2 ts)'

也就是说

  • ts[Tree a] 类型
  • 您将它用作foldr 的第三个参数
  • foldr 期望它的第三个参数是 [c] 类型
  • [c][Tree a] 是不同的类型,因此这是一个错误

所以你需要将ts 处理成[c] 类型的东西,并将结果传递给foldr 而不是ts 本身。 map 函数将是一个很好的起点。

【讨论】:

  • 感谢您为我指明了正确的方向,但我还没有找到解决方案。我对fg 应该做什么感到困惑。在第一篇文章中查看我的编辑...
  • 在您的编辑中,添加您现在收到的完整错误消息,以及您理解它的含义。如果我们不必自己尝试编译您的代码,那么我们为您提供帮助会容易得多。
  • Hinton:我最终得到了一个有效的treefoldr,但foldl 通用版本继续给我这个错误(请参阅我的编辑)。感谢您的建议。
  • @Gremo:您对treefoldl 的类型定义是错误的,正如错误所说,问题出在g 函数上。您在 foldl 中使用它,因此结果的类型和第一个参数必须相同。
【解决方案2】:

我不知道你的作业是否允许这样的解决方案,但是当类型类的使用没问题时,你可以写

import Data.Foldable
import Data.Monoid

data Tree a = Void | Node a [Tree a]
    deriving (Eq, Show)


instance Foldable Tree where 
   foldMap f Void = mempty
   foldMap f (Node value []) = f value
   foldMap f (Node value (x:xs)) = foldMap f x `mappend` foldMap f (Node value xs)

使用这个定义,你的函数的实现应该是微不足道的,因为 Foldable 定义了 foldl、foldr 等。

【讨论】:

    【解决方案3】:

    我和你的教授谈过,最后我找到了正确的解决方案:

    treefoldr :: (Eq a, Show a) => (a -> b -> c) -> c -> (c -> b -> b) -> b -> Tree a -> c
    treefoldr _ z1 _ _   Void      = z1
    treefoldr f z1 g z2 (Node a ts) = f a $ foldr (aggr) z2 ts
        where
            aggr t z = g (treefoldr f z1 g z2 t) z
    
    treefoldl :: (Eq a, Show a) => (b -> a -> c) -> c -> (c -> b -> b) -> b -> Tree a -> c
    treefoldl _ z1 _ _   Void      = z1
    treefoldl f z1 g z2 (Node a ts) = f (foldl (aggr) z2 ts) a
        where
            aggr z t = g (treefoldl f z1 g z2 t) z
    

    问候

    【讨论】:

      猜你喜欢
      • 2015-01-08
      • 1970-01-01
      • 2016-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多