使用折叠在树上实现地图

时间:2015-06-23 15:13:37

标签: scala haskell

我正在尝试使用fold实现地图。我可以在Haskell

中这样做
 data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show)


 foldTree :: Tree a -> b -> (b -> a -> b -> b) ->  b   
 foldTree EmptyTree d _ = d
 foldTree (Node a l r) d f =  f (foldTree l d f)  a  (foldTree r d f)


 mapTree :: Tree a -> ( a -> b) -> Tree b
 mapTree tree f = foldTree tree EmptyTree (\l n r -> Node (f n)  l  r)

但是,当我尝试将其移植到Scala时,我有点卡住了

sealed trait Tree[+A]
case object EmptyTree extends Tree[Nothing]
case class Node[A](value: A , left: Tree[A], right: Tree[A]) extends Tree[A]


def fold[A, B](t:Tree[A] , z:B)(f:(B,A,B) => B) : B = t match {
    case EmptyTree => z 
    case Node(x,l,r) => f ( fold( l , z )(f) , x , fold( r , z )(f) )   
  }

  def map(tree:Tree[Int])(f:Int=>Int) : Tree[Int] = fold(tree , EmptyTree)((l,x,r) => Node(f(x),l,r))

编译器抱怨它期望函数I中的EmptyTree传递给折叠。

fold(tree , EmptyTree)((l,x,r) => Node(f(x),l,r))

Map的返回类型是Tree,所以我希望这可以工作。有什么建议吗?

2 个答案:

答案 0 :(得分:2)

尝试将最后一行写为

def map(tree:Tree[Int])(f:Int=>Int) : Tree[Int] = fold(tree , EmptyTree:Tree[Int])((l,x,r) => Node(f(x),l,r))

与haskell相比,Scala的类型推断是非常有限的,在这种情况下,它试图从左到右的参数中推断fold的类型,并且直接决定折叠的结果类型应该是EmptyTree并且不是Tree[Int]。通常将辅助构造函数添加到父类型的伴随对象有助于这种情况,例如在Option对象中有一个构造函数

def empty[A]: Option[A] 

返回父类型。

答案 1 :(得分:2)

作为@ vitalii解决方案的替代方案,请将类型参数设为fold

def map(tree: Tree[Int])(f: Int=>Int): Tree[Int] =
  fold[Int, Tree[Int]](tree, EmptyTree)((l, x, r) => Node[Int](f(x), l, r))
  //   ^^^^^^^^^^^^^^