一个适用,您可以在上下文中的上下文应用函数的值。例如,您可以将some((i: Int) => i + 1)
应用于some(3)
并获得some(4)
。现在让我们忘记。我会在稍后回来。
名单有两种表示方法,它要么Nil
或head :: tail
。您可以使用采用foldLeft
折叠过,但还有另一种方式来折叠它:
def foldr[A, B](l: List[A], acc0: B, f: (A, B) => B): B = l match {
case Nil => acc0
case x :: xs => f(x, foldr(xs, acc0, f))
}
鉴于List(1, 2)
我们折叠在应用列表中的从右侧开始的功能 - 即使我们真的解构名单从左侧!
f(1, f(2, Nil))
这可以用来计算列表的长度。鉴于List(1, 2)
:
foldr(List(1, 2), 0, (i: Int, acc: Int) => 1 + acc)
// returns 2
这也可以用来创建另一个列表:
foldr[Int, List[Int]](List(1, 2), List[Int](), _ :: _)
//List[Int] = List(1, 2)
因此,考虑空单,我们能够创造另一个列表中::
功能。如果我们的元素在的情况下怎么办?如果我们的背景是一个应用,那么我们仍然可以应用我们的元素和::
。继续List(1, 2)
和Option
作为我们的应用。我们从some(List[Int]()))
开始,我们想要在Option
上下文中应用::
函数。这就是F.map2
所做的。它在Option
上下文中取两个值,将提供的两个参数的函数放入Option
上下文中并将它们应用到一起。
所以环境之外,我们有(2, Nil) => 2 :: Nil
在情况下,我们有:(Some(2), Some(Nil)) => Some(2 :: Nil)
让我们回到原来的问题:
// do a foldr
DList.fromList(l).foldr(F.point(List[B]())) {
// starting with an empty list in its applicative context F.point(List[B]())
(a, fbs) => F.map2(f(a), fbs)(_ :: _)
// Apply the `::` function to the two values in the context
}
我不知道为什么区别DList
使用。我看到的是,它使用蹦床很希望能够使这个实现工作而不用吹捧堆栈,但是我没有试过,所以我不知道。
有关像这样实现正确折叠的有趣部分是我认为它给你提供了一种使用catamorphisms来实现代数数据类型遍历的方法。
例如给出:
trait Tree[+A]
object Leaf extends Tree[Nothing]
case class Node[A](a: A, left: Tree[A], right: Tree[A]) extends Tree[A]
折就这样被定义(这实在是遵循同样的方法作为List
):
def fold[A, B](tree: Tree[A], valueForLeaf: B, functionForNode: (A, B, B) => B): B = {
tree match {
case Leaf => valueForLeaf
case Node(a, left, right) => functionForNode(a,
fold(left, valueForLeaf, functionForNode),
fold(right, valueForLeaf, functionForNode)
)
}
}
和遍历将使用fold
与F.point(Leaf)
和将其应用于Node.apply
。虽然没有F.map3
,所以它可能有点麻烦。
非常棒的答案,不需要任何外部知识 – betehess 2012-03-15 13:21:40