我正在尝试使用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,所以我希望这可以工作。有什么建议吗?
答案 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))
// ^^^^^^^^^^^^^^