函数式编程:foldLeft是所有函数方法的父方法,如foldRight,map,filter

时间:2017-03-11 11:57:04

标签: scala functional-programming

我正在学习Scala和功能编程世界的新人。我发现大多数方法,例如foldRightmapfilterreduce ...总是可以由foldLeft编写(由foldLeft书写foldRight)用于使用尾递归)。

例如:

def map[A, B](l: List[A])(f: A => B): List[B] =
    foldRight(l, Nil: List[B])((x, xs) => Cons(f(x), xs))

def reverse[A](l: List[A]): List[A] =
    foldLeft(l, l)((xs, x) => Cons(x, xs))

def foldRight[A, B](l: List[A], z: B)(f: (A, B) => B): B =
    foldLeft(reverse(l), z)((xs, x) => f(x, xs))

在我进入函数式编程世界之前,我认为mapreduce是函数式编程的两种基本方法。从这两种方法中,我可以构建更复杂的方法。但基于上面的例子,我认为foldLeft应该是所有功能方法的基础,例如map,filter,...

在函数式编程世界中是这样吗?

由于

3 个答案:

答案 0 :(得分:2)

一般来说,不,foldLeft不是"父亲"所有其他方法,因为除了List之外还有许多集合。例如,您无法通过Stream.map实施Stream.foldLeft,因为foldLeft执行完整遍历,但map不应该遍历流,因为它的懒惰(它可能是无限的)

至于List,你大多是对的。但是,如果通过foldLeft实现,某些方法效率极低。考虑list.drop(1)。它应该只返回列表的尾部,但是foldLeft会进行完整的遍历。

答案 1 :(得分:2)

我要将我的评论扩展为答案。

让我们从名为 Bunch [T] 的类型开始,它是 Traversable [T] ,是所有Scala集合的基本特征。它实现了所有常见的monadic东西,如 map [U](f:T => U)等。

现在让我们开始根据他们的签名对所有方法进行分类。在下文中, U T 的超类型:

  • 遍历:实际上有两个子类:
    • 保持形状:这些定义了 Bunch [U] 的返回类型;示例: map ;
    • 非形状保留:这些定义了 Iterator [T] 的返回类型, Traversable [T] Traversable [U] 等;示例: iterator ;
  • 选择:这些定义了 T 的返回类型;示例 head ;
  • maybeSelecting :这些定义了选项[U]的返回类型
  • 聚合:这些定义了 U 的返回类型;示例: foldLeft ;
  • testing :这些定义了 Boolean 的返回类型;示例:清空;
  • 副作用:这些定义了 Unit 的返回类型;示例 foreach ;
  • 等。等

这些方法中的每一种都与其他方法有根本的不同。某些(例如聚合)需要对集合进行完整遍历(例如,在 Stream 上无法实现)。其他人没有。

我们可以为每个方法类定义一个“父”方法。例如,我称之为“副作用”的类I的父方法显然是 foreach 。我认为将 foldLeft 称为“聚合”方法的“父亲”可能是合理的。但我认为,总的来说,我们一定会遇到这种方法的问题。您不能总是根据另一个方法定义一个方法。 map 本身可以根据其他方法定义,但它需要两个: flatMap 和“unit”,一个构造函数方法,因此不在我的列表中。

答案 2 :(得分:1)

从某种意义上说,你是对的。在他1999年的论文A tutorial on the expressiveness and universality of fold中,Graham Hutton回顾了如何使用fold来证明东西而不使用递归和定义递归函数,以显示这个函数的表达方式。