Scala中foldLeft和reduceLeft的区别

发布于 2024-12-09 13:03:34 字数 275 浏览 6 评论 0原文

我已经了解了 foldLeftreduceLeft 之间的基本区别

foldLeft:

  • 必须传递初始值

reduceLeft:

  • 首先采用 作为初始值的集合元素
  • 如果集合为空,

会引发异常还有其他区别吗?

有什么具体原因让两种方法具有相似的功能?

I have learned the basic difference between foldLeft and reduceLeft

foldLeft:

  • initial value has to be passed

reduceLeft:

  • takes first element of the collection as initial value
  • throws exception if collection is empty

Is there any other difference ?

Any specific reason to have two methods with similar functionality?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

我们的影子 2024-12-16 13:03:35

它们都在 Scala 标准库中的根本原因可能是因为它们都在 Haskell 标准库中(称为 foldlfoldl1)。如果 reduceLeft 不是,它通常会在不同的项目中被定义为一种方便的方法。

The basic reason they are both in Scala standard library is probably because they are both in Haskell standard library (called foldl and foldl1). If reduceLeft wasn't, it would quite often be defined as a convenience method in different projects.

逆夏时光 2024-12-16 13:03:35

来自 Scala 函数式编程原则 (Martin Odersky):

函数reduceLeft是根据更通用的函数foldLeft定义的。

foldLeftreduceLeft 类似,但采用累加器 z 作为附加参数,当foldLeft 在空列表上调用:

(列表(x1,...,xn)foldLeft z)(op)=(...(z op x1)op ...)op x

[相对于reduceLeft,在空列表上调用时会引发异常。]

该课程(参见讲座 5.5)提供了这些函数的抽象定义,这说明了它们的差异,尽管它们在模式的使用上非常相似匹配和递归。

abstract class List[T] { ...
  def reduceLeft(op: (T,T)=>T) : T = this match{
    case Nil     => throw new Error("Nil.reduceLeft")
    case x :: xs => (xs foldLeft x)(op)
  }
  def foldLeft[U](z: U)(op: (U,T)=>U): U = this match{
    case Nil     => z
    case x :: xs => (xs foldLeft op(z, x))(op)
  }
}

请注意,foldLeft 返回一个 U 类型的值,该值不一定与 List[T] 类型相同,但 reduceLeft 返回一个值与列表类型相同)。

From Functional Programming Principles in Scala (Martin Odersky):

The function reduceLeft is defined in terms of a more general function, foldLeft.

foldLeft is like reduceLeft but takes an accumulator z, as an additional parameter, which is returned when foldLeft is called on an empty list:

(List (x1, ..., xn) foldLeft z)(op) = (...(z op x1) op ...) op x

[as opposed to reduceLeft, which throws an exception when called on an empty list.]

The course (see lecture 5.5) provides abstract definitions of these functions, which illustrates their differences, although they are very similar in their use of pattern matching and recursion.

abstract class List[T] { ...
  def reduceLeft(op: (T,T)=>T) : T = this match{
    case Nil     => throw new Error("Nil.reduceLeft")
    case x :: xs => (xs foldLeft x)(op)
  }
  def foldLeft[U](z: U)(op: (U,T)=>U): U = this match{
    case Nil     => z
    case x :: xs => (xs foldLeft op(z, x))(op)
  }
}

Note that foldLeft returns a value of type U, which is not necessarily the same type as List[T], but reduceLeft returns a value of the same type as the list).

愿与i 2024-12-16 13:03:35

要真正了解您在使用折叠/减少做什么,
检查这个:http://wiki.tcl.tk/17983
很好的解释。一旦你掌握了折叠的概念,
减少将与上面的答案一起:
list.tail.foldLeft(list.head)(_)

To really understand what are you doing with fold/reduce,
check this: http://wiki.tcl.tk/17983
very good explanation. once you get the concept of fold,
reduce will come together with the answer above:
list.tail.foldLeft(list.head)(_)

小鸟爱天空丶 2024-12-16 13:03:35

Scala 2.13.3,演示:

val names = List("Foo", "Bar")
println("ReduceLeft: "+ names.reduceLeft(_+_))
println("ReduceRight: "+ names.reduceRight(_+_))
println("Fold: "+ names.fold("Other")(_+_))
println("FoldLeft: "+ names.foldLeft("Other")(_+_))
println("FoldRight: "+ names.foldRight("Other")(_+_))

输出:

ReduceLeft: FooBar
ReduceRight: FooBar
Fold: OtherFooBar
FoldLeft: OtherFooBar
FoldRight: FooBarOther

Scala 2.13.3, Demo:

val names = List("Foo", "Bar")
println("ReduceLeft: "+ names.reduceLeft(_+_))
println("ReduceRight: "+ names.reduceRight(_+_))
println("Fold: "+ names.fold("Other")(_+_))
println("FoldLeft: "+ names.foldLeft("Other")(_+_))
println("FoldRight: "+ names.foldRight("Other")(_+_))

outputs:

ReduceLeft: FooBar
ReduceRight: FooBar
Fold: OtherFooBar
FoldLeft: OtherFooBar
FoldRight: FooBarOther
三月梨花 2024-12-16 13:03:34

在给出实际答案之前,这里要提一下:

  • 您的问题与 left 没有任何关系,而是关于减少和折叠之间的区别。
  • 区别根本不在于实现,而在于看看签名。
  • 这个问题与 Scala 没有任何关系,而是关于函数式编程的两个概念。

回到你的问题:

这是 foldLeft 的签名(也可以是 foldRight 来表达我要表达的观点)

def foldLeft [B] (z: B)(f: (B, A) => B): B

:这是 reduceLeft 的签名(这里方向并不重要)

def reduceLeft [B >: A] (f: (B, A) => B): B

这两个看起来非常相似,因此引起了混乱。 reduceLeftfoldLeft 的特例(顺便说一句,这意味着您有时可以使用它们中的任何一个来表达相同的内容)。

当您在 List[Int] 上调用 reduceLeft 时,它实际上会将整个整数列表减少为单个值,该值的类型为 Int (或 Int 的超类型,因此 [B >: A])。

当您在 List[Int] 上调用 foldLeft 时,它会将整个列表(想象一下卷起一张纸)折叠成单个值,但该值不会折叠甚至必须与Int相关(因此是[B])。

下面是一个示例:

def listWithSum(numbers: List[Int]) = numbers.foldLeft((List.empty[Int], 0)) {
   (resultingTuple, currentInteger) =>
      (currentInteger :: resultingTuple._1, currentInteger + resultingTuple._2)
}

此方法采用 List[Int] 并返回 Tuple2[List[Int], Int](List[Int], Int )。它计算总和并返回一个包含整数列表及其总和的元组。顺便说一下,列表是向后返回的,因为我们使用了 foldLeft 而不是 foldRight

观看一折统治一切以获得更深入的解释。

Few things to mention here, before giving the actual answer:

  • Your question doesn't have anything to do with left, it's rather about the difference between reducing and folding
  • The difference is not the implementation at all, just look at the signatures.
  • The question doesn't have anything to do with Scala in particular, it's rather about the two concepts of functional programming.

Back to your question:

Here is the signature of foldLeft (could also have been foldRight for the point I'm going to make):

def foldLeft [B] (z: B)(f: (B, A) => B): B

And here is the signature of reduceLeft (again the direction doesn't matter here)

def reduceLeft [B >: A] (f: (B, A) => B): B

These two look very similar and thus caused the confusion. reduceLeft is a special case of foldLeft (which by the way means that you sometimes can express the same thing by using either of them).

When you call reduceLeft say on a List[Int] it will literally reduce the whole list of integers into a single value, which is going to be of type Int (or a supertype of Int, hence [B >: A]).

When you call foldLeft say on a List[Int] it will fold the whole list (imagine rolling a piece of paper) into a single value, but this value doesn't have to be even related to Int (hence [B]).

Here is an example:

def listWithSum(numbers: List[Int]) = numbers.foldLeft((List.empty[Int], 0)) {
   (resultingTuple, currentInteger) =>
      (currentInteger :: resultingTuple._1, currentInteger + resultingTuple._2)
}

This method takes a List[Int] and returns a Tuple2[List[Int], Int] or (List[Int], Int). It calculates the sum and returns a tuple with a list of integers and it's sum. By the way the list is returned backwards, because we used foldLeft instead of foldRight.

Watch One Fold to rule them all for a more in depth explanation.

中二柚 2024-12-16 13:03:34

reduceLeft 只是一个方便的方法。它相当于

list.tail.foldLeft(list.head)(_)

reduceLeft is just a convenience method. It is equivalent to

list.tail.foldLeft(list.head)(_)
划一舟意中人 2024-12-16 13:03:34

foldLeft 更通用,您可以使用它来生成与最初放入的内容完全不同的内容。而 reduceLeft 只能生成相同类型或超类型的最终结果的集合类型。例如:

List(1,3,5).foldLeft(0) { _ + _ }
List(1,3,5).foldLeft(List[String]()) { (a, b) => b.toString :: a }

foldLeft 将应用最后折叠结果(第一次使用初始值)和下一个值的闭包。

另一方面,reduceLeft 将首先组合列表中的两个值并将它们应用于闭包。接下来它将把其余的值与累积结果结合起来。请参阅:

List(1,3,5).reduceLeft { (a, b) => println("a " + a + ", b " + b); a + b }

如果列表为空,foldLeft 可以将初始值呈现为合法结果。另一方面,如果 reduceLeft 在列表中找不到至少一个值,则它没有合法值。

foldLeft is more generic, you can use it to produce something completely different than what you originally put in. Whereas reduceLeft can only produce an end result of the same type or super type of the collection type. For example:

List(1,3,5).foldLeft(0) { _ + _ }
List(1,3,5).foldLeft(List[String]()) { (a, b) => b.toString :: a }

The foldLeft will apply the closure with the last folded result (first time using initial value) and the next value.

reduceLeft on the other hand will first combine two values from the list and apply those to the closure. Next it will combine the rest of the values with the cumulative result. See:

List(1,3,5).reduceLeft { (a, b) => println("a " + a + ", b " + b); a + b }

If the list is empty foldLeft can present the initial value as a legal result. reduceLeft on the other hand does not have a legal value if it can't find at least one value in the list.

浪荡不羁 2024-12-16 13:03:34

作为参考,如果应用于空容器,reduceLeft 将出错并出现以下错误。

java.lang.UnsupportedOperationException: empty.reduceLeft

重新编写要使用的代码

myList foldLeft(List[String]()) {(a,b) => a+b}

是一种潜在的选择。另一种方法是使用 reduceLeftOption 变体,它返回 Option 包装的结果。

myList reduceLeftOption {(a,b) => a+b} match {
  case None    => // handle no result as necessary
  case Some(v) => println(v)
}

For reference, reduceLeft will error if applied to an empty container with the following error.

java.lang.UnsupportedOperationException: empty.reduceLeft

Reworking the code to use

myList foldLeft(List[String]()) {(a,b) => a+b}

is one potential option. Another is to use the reduceLeftOption variant which returns an Option wrapped result.

myList reduceLeftOption {(a,b) => a+b} match {
  case None    => // handle no result as necessary
  case Some(v) => println(v)
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文