Подтвердить что ты не робот

Разница между foldLeft и reduceLeft в Scala

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

foldLeft:

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

reduceLeft:

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

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

Любая конкретная причина иметь два метода с аналогичной функциональностью?

4b9b3361

Ответ 1

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

  • Ваш вопрос не имеет ничего общего с 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.empty[Int], 0)) {
   (resultingTuple, currentInteger) =>
      (currentInteger :: resultingTuple._1, currentInteger + resultingTuple._2)
}

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

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

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

Ответ 5

Для справки, 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)
}

Ответ 6

От Принципы функционального программирования в Scala (Martin Odersky):

Функция 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], но reduceLeft возвращает значение того же типа, что и список).

Ответ 7

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