Я узнал основную разницу между foldLeft
и reduceLeft
foldLeft:
reduceLeft:
Есть ли другая разница?
Любая конкретная причина иметь два метода с аналогичной функциональностью?
Немногие, о которых нужно упомянуть, прежде чем дать реальный ответ:
left
, а скорее о различии между уменьшением и складываниемВернуться к вашему вопросу:
Вот подпись 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
.
B
является супертипом A
? Кажется, что B
самом деле должен быть подтипом A
, а не супертипом. Например, если предположить Banana <: Fruit <: Food
, если бы у нас был список Fruit
, кажется, что он может содержать некоторые Banana
s, но если он содержит какие-либо Food
то тип будет Food
, правильно? Таким образом, в этом случае, если B
является супертипом A
и существует список, содержащий как B
s, так и A
s, список должен иметь тип B
, а не A
Можете ли вы объяснить это несоответствие?
List[Banana]
может быть уменьшен до одного Banana
или одного Fruit
или одной Food
. Потому что Fruit :> Banana
и `еда:> банан '.
reduceLeft
- просто удобный метод. Это эквивалентно
list.tail.foldLeft(list.head)(_)
reducelft
бы, чтобы исправить правописание
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
, с другой стороны, не имеет юридического значения, если он не может найти хотя бы одно значение в списке.
Для справки, 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)
}
Основная причина, по которой они оба находятся в стандартной библиотеке Scala, вероятно, потому, что они оба находятся в стандартной библиотеке Haskell (называемые foldl
и foldl1
). Если reduceLeft
не было, это довольно часто было бы определено как метод удобства в разных проектах.
От Принципы функционального программирования в 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 возвращает значение того же типа, что и список).
Чтобы понять, что вы делаете с помощью fold/reduce, проверьте это: http://wiki.tcl.tk/17983 очень хороший информация. как только вы получите концепцию складки, сокращение будет сопровождаться ответом выше: list.tail.foldLeft(list.head) (_)