it-roy-ru.com

разница между foldLeft и reduLeft в Scala

Я узнал основную разницу между foldLeft и reduceLeft

foldLeft:

  • начальное значение должно быть передано

reduceLeft:

  • принимает первый элемент коллекции в качестве начального значения
  • выдает исключение, если коллекция пуста

Есть ли другая разница?

Есть ли какая-то конкретная причина иметь два метода с похожей функциональностью?

181
Rajesh Pitty

Несколько вещей, чтобы упомянуть здесь, прежде чем дать фактический ответ:

  • Ваш вопрос не имеет никакого отношения к left, он скорее касается разницы между уменьшением и сворачиванием
  • Разница вовсе не в реализации, просто посмотрите на подписи.
  • Этот вопрос не имеет ничего общего с Scala, в частности, он касается двух концепций функционального программирования.

Вернуться к вашему вопросу:

Вот подпись foldLeft (также могла быть foldRight для пункта, который я собираюсь сделать):

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

А вот и подпись reduceLeft (опять же направление здесь не имеет значения)

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

Эти два выглядят очень похожими и, таким образом, вызвали путаницу. reduceLeft - это особый случай foldLeft (что, кстати, означает, что вы иногда можете выразить то же самое, используя любой из них).

Когда вы вызываете reduceLeft скажем для List[Int], это буквально сократит весь список целых чисел до одного значения, которое будет иметь тип Int (или супертип Int, следовательно, [B >: A]).

Когда вы вызываете foldLeft скажем для List[Int], он свернет весь список (представьте, что вы катите лист бумаги) в одно значение, но это значение не обязательно должно быть даже связано с Int (следовательно, [B]).

Вот пример:

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

Этот метод принимает List[Int] и возвращает Tuple2[List[Int], Int] или (List[Int] -> Int). Он вычисляет сумму и возвращает кортеж со списком целых чисел и его суммой. Кстати, список возвращается в обратном порядке, потому что мы использовали foldLeft вместо foldRight.

Смотрите один раз, чтобы управлять ими всеми для более подробного объяснения.

280
agilesteel

reduceLeft - это просто удобный метод. Это эквивалентно

list.tail.foldLeft(list.head)(_)
182
Kim Stebel

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, с другой стороны, не имеет допустимого значения, если не может найти хотя бы одно значение в списке.

43
thoredge

Для справки, reduceLeft произойдет ошибка, если применить к пустому контейнеру со следующей ошибкой.

Java.lang.UnsupportedOperationException: empty.reduceLeft

Переработка кода для использования

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

это один из возможных вариантов. Другой вариант - использовать вариант reduceLeftOption, который возвращает результат, заключенный в опцию.

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

Основная причина, по которой они оба находятся в стандартной библиотеке Scala, вероятно, заключается в том, что они оба находятся в стандартной библиотеке Haskell (называемой foldl и foldl1). Если бы reduceLeft не было, его часто определяли как удобный метод в разных проектах.

5
Alexey Romanov

От Принципы функционального программирования в Scala (Мартин Одерский):

Функция reduceLeft определена в терминах более общей функции foldLeft.

foldLeft похож на reduceLeft, но принимает аккумулятор z, в качестве дополнительного параметра, который возвращается, когда foldLeft вызывается в пустом списке:

(List (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], но reduLeft возвращает значение того же типа, что и список).

2
C8H10N4O2

Чтобы действительно понять, что вы делаете со сложением/уменьшением, проверьте это: http://wiki.tcl.tk/1798 очень хорошее объяснение. как только вы поймете концепцию сгиба, сокращение придет вместе с ответом выше: list.tail.foldLeft (list.head) (_)

0
Henry H.