Scala区分折叠函数和约简函数有什么客观原因吗?



Reduce可以是不接受第一个元素的重写折叠。我想这个设计决定是有答案的,但是我找不到。

这两种操作根本不同。给它们起同样的名字会让人困惑。在Ruby中,等效的方法被称为Enumerable#inject(别名为Enumerable#reduce),并且被重载以像Scala的scala.collection.Iterable.foldLeft[B](z: B)(op: (B, A) => B): Bscala.collection.Iterable.reduceLeft[B >: A](op: (B, A) => B): B一样工作。你可以看到这在ruby中引起的混乱。

[注意:从这里开始,我将使用Scala术语来指代两种不同的操作,即foldreduce。]

这两种操作的区别如下:
  • fold适用于空集合,reduce不适用。在Scala中,它要么抛出UnsupportedOperationException,要么使用scala.collection.Iterable.reduceLeftOption[B >: A](op: (B, A) => B): Option[B]。在Haskell中,Scala调用的reduceLeft被称为Data.List.foldl1 :: Foldable t => (a -> a -> a) -> t a -> a,因为你至少需要1元素。
  • 对于reduce,结果类型与元素类型相同(或者更准确地说,是元素类型的超类型),而对于fold,结果类型可以是任何。例如,不能用reduce对字符串集合的长度求和,因为reduce对字符串集合的结果类型不能是数字,必须是字符串。
  • 最重要的一个:fold是一个一般的迭代操作,意思是,你可以用循环或递归做的一切,你可以用fold做。如果你有fold,你就不需要map,不需要filter,不需要forall,不需要groupBy,不需要scan,等等,当然你也不需要reduce。您可以使用fold实现所有这些。(我曾经为Ruby这样做过一次。)reduce不是一般,你不能例如实现任何集合转换(例如mapfilter或甚至只是reverse),因为reduce的结果类型是元素类型,不能是集合类型。

因为这两个操作是如此不同,所以给它们不同的名字是有意义的:Scala中的foldreduce, Haskell中的foldXfoldX1,等等。在我最熟悉的语言Ruby中,它们有相同的名称,这导致了混淆。

简而言之,它们具有不同的类型约束,因此具有不同的类型签名。fold的结果可以是任何你想要的。reduce的结果必须是元素的超类型。

这就是为什么foldreduce在动态类型语言中通常是组合在一起的,而在静态类型语言中是分开的。

最新更新