应用程序允许您将上下文中的函数应用于上下文中的值。因此,例如,您可以申请some((i: Int) => i + 1)
并some(3)
获得some(4)
. 让我们暂时忘记这一点。我稍后再谈。
List 有两种表示形式,要么是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]
Fold 将像这样定义(实际上遵循与 for 相同的方法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)
)
}
}
并且 traverse 将使用它fold
并将F.point(Leaf)
其应用于Node.apply
. 虽然没有F.map3
所以可能有点麻烦。