通过Scala中的FoldLeft读取FoldRight

时间:2019-03-10 06:12:05

标签: scala functional-dependencies

作者在Functional Programming in Scala中要求通过FoldLeft表达FoldRight。然后作者提供以下implementation

  def foldRightViaFoldLeftAuthor[A, B](l: List[A], z: B)(f: (A, B) => B): B = {
    foldLeft(l, (b: B) => b)((g, a) => b => g(f(a, b)))(z)
  }

this之类的几个问题要求解释作者的解决方案。也许很多人仍在努力理解它。

当我在考虑任务时,我想出了一个不同的实现,至少对我来说似乎更具可读性和易掌握性

  def foldRightViaFoldLeftMy[A, B](l: List[A], z: B)(f: (A, B) => B): B = {
    foldLeft(l, z)(((g: (A, B) => B) => (b: B, a: A) => g(a, b)) (f))
  }

所以我基本上准备了一个将f(a,b)转换为f(b,a)的函数,现在我可以调用foldLeft了,它是尾递归的。

所以我的问题是:

  1. 是否有任何理由以作者的方式实施它?
  2. 与作者相比,我的实现中是否有任何弊端?

1 个答案:

答案 0 :(得分:3)

您编写的实现具有与foldRight相同的签名,但是当组合操作不可交换时,它的语义不正确。举个例子,空列表为零且组合操作为弊的右折应为identity:

scala> val input = List(1, 2, 3)
input: List[Int] = List(1, 2, 3)

scala> val f: (Int, List[Int]) => List[Int] = _ :: _
f: (Int, List[Int]) => List[Int] = $$Lambda$1912/991363637@5e9bf744

scala> foldRightViaFoldLeftAuthor(input, List.empty[Int])(f)
res0: List[Int] = List(1, 2, 3)

但是您的实现会颠倒列表:

scala> foldRightViaFoldLeftMy(input, List.empty[Int])(f)
res1: List[Int] = List(3, 2, 1)

这是因为即使切换了组合函数的参数顺序,您仍在从左向右折叠。我发现the Wikipedia page about fold上的图表对于可视化差异很有用。在您的实现中,应用程序是这样发生的:

scala> f(3, f(2, f(1, Nil)))
res2: List[Int] = List(3, 2, 1)

在本书的实现中,您会有类似以下内容:

((b3: List[Int]) =>
  ((b2: List[Int]) =>
    ((b1: List[Int]) => identity(f(1, b1)))(f(2, b2)))(f(3, b3)
  )
)(Nil)

归结为:

scala> f(1, f(2, f(3, Nil)))
res3: List[Int] = List(1, 2, 3)

因此,您对这两个问题的回答都是“是”,您的实现与本书的实现之间存在重要区别。