Scalaz为各种ADT提供了一个名为fold
的方法,例如Boolean
,Option[_]
,Validation[_, _]
,Either[_, _]
等。此方法基本上采用了对应的函数给定ADT的所有可能情况。换句话说,模式匹配如下所示:
x match {
case Case1(a, b, c) => f(a, b, c)
case Case2(a, b) => g(a, b)
.
.
case CaseN => z
}
相当于:
x.fold(f, g, ..., z)
一些例子:
scala> (9 == 8).fold("foo", "bar")
res0: java.lang.String = bar
scala> 5.some.fold(2 *, 2)
res1: Int = 10
scala> 5.left[String].fold(2 +, "[" +)
res2: Any = 7
scala> 5.fail[String].fold(2 +, "[" +)
res6: Any = 7
同时,Traversable[_]
类型有一个同名的操作,它遍历集合对其元素执行某些操作,并累积结果值。例如,
scala> List(2, 90, 11).foldLeft("Contents: ")(_ + _.toString + " ")
res9: java.lang.String = "Contents: 2 90 11 "
scala> List(2, 90, 11).fold(0)(_ + _)
res10: Int = 103
scala> List(2, 90, 11).fold(1)(_ * _)
res11: Int = 1980
为什么这两个操作被标识为同一个名字 - fold
/ catamorphism?我没有看到两者之间有任何相似之处/关系。我错过了什么?
答案 0 :(得分:7)
我认为你遇到的问题是你看到这些东西是基于它们的实现,而不是它们的类型。考虑这种简单的类型表示:
List[A] = Nil
| Cons head: A tail: List[A]
Option[A] = None
| Some el: A
现在,让我们考虑Option
的折叠:
fold[B] = (noneCase: => B, someCase: A => B) => B
因此,在Option
上,它会将所有可能的情况减少到B
中的某个值,并返回该值。现在,让我们看一下List
:
fold[B] = (nilCase: => B, consCase: (A, List[A]) => B) => B
但请注意,我们在List[A]
上有一个递归调用。我们必须以某种方式折叠,但我们知道fold[B]
上的List[A]
将始终返回B
,因此我们可以像这样重写它:
fold[B] = (nilCase: => B, consCase: (A, B) => B) => B
换句话说,我们将List[A]
替换为B
,因为在给定B
的类型签名的情况下折叠它将始终返回fold
。现在,让我们看看foldRight
的Scala(用例)类型签名:
foldRight[B](z: B)(f: (A, B) ⇒ B): B
说,这会让你想起什么吗?
答案 1 :(得分:5)
如果您认为“折叠”为“通过操作使用种子值缩小容器中的所有值”,并且您认为Option可以作为最多只能包含一个值的容器,那么这将开始有意义。
实际上,foldLeft
具有相同的签名,并且如果您在空列表中使用它而无效,并且在仅包含一个元素的列表上使用它,则会给出完全相同的结果:
scala> val opt : Option[Int] = Some(10)
opt: Option[Int] = Some(10)
scala> val lst : List[Int] = List(10)
lst: List[Int] = List(10)
scala> opt.foldLeft(1)((a, b) => a + b)
res11: Int = 11
scala> lst.foldLeft(1)((a, b) => a + b)
res12: Int = 11
fold
也在Scala标准库中的List
和Option
上定义,具有相同的签名(我相信它们都从特征中继承它)。再一次,你在单身人士名单上获得与在某些人身上相同的结果:
scala> opt.fold(1)((a, b) => a * b)
res25: Int = 10
scala> lst.fold(1)((a, b) => a * b)
res26: Int = 10
我对fold
/ Option
/等的Scalaz的Either
不是100%肯定,你在那里提出了一个很好的观点。它似乎与我习惯的“折叠”具有完全不同的签名和操作。