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

132

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

foldLeft:

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

reduceLeft:

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

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

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

Теги:
functional-programming
higher-order-functions
fold

7 ответов

245
Лучший ответ

Немногие, о которых нужно упомянуть, прежде чем дать реальный ответ:

  • Ваш вопрос не имеет ничего общего с 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 say на 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] и возвращает a Tuple2[List[Int], Int] или (List[Int] -> Int). Он вычисляет сумму и возвращает кортеж со списком целых чисел и суммой. Кстати, список возвращается назад, потому что мы использовали foldLeft вместо foldRight.

  • 0
    Можете ли вы объяснить, почему B является супертипом A ? Кажется, что B самом деле должен быть подтипом A , а не супертипом. Например, если предположить Banana <: Fruit <: Food , если бы у нас был список Fruit , кажется, что он может содержать некоторые Banana s, но если он содержит какие-либо Food то тип будет Food , правильно? Таким образом, в этом случае, если B является супертипом A и существует список, содержащий как B s, так и A s, список должен иметь тип B , а не A Можете ли вы объяснить это несоответствие?
  • 0
    Я не уверен, правильно ли я понимаю ваш вопрос. Мой пятилетний ответ говорит о функции уменьшения, что List[Banana] может быть уменьшен до одного Banana или одного Fruit или одной Food . Потому что Fruit :> Banana и `еда:> банан '.
Показать ещё 1 комментарий
162

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

list.tail.foldLeft(list.head)(_)
  • 11
    Хороший, краткий ответ :) reducelft бы, чтобы исправить правописание
  • 0
    @hanxue: спасибо, готово
Показать ещё 1 комментарий
42

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

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

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

1

От Принципы функционального программирования в 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 возвращает значение того же типа, что и список).

0

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню