web-dev-qa-db-fra.com

différence entre foldLeft et reductionLeft dans Scala

J'ai appris la différence fondamentale entre foldLeft et reduceLeft

foldLeft:

  • la valeur initiale doit être passée

reductionLeft:

  • prend le premier élément de la collection comme valeur initiale
  • lève une exception si la collection est vide

Y a-t-il une autre différence?

Une raison spécifique d'avoir deux méthodes avec des fonctionnalités similaires?

184
Rajesh Pitty

Peu de choses à mentionner ici, avant de donner la réponse réelle:

  • Votre question n'a rien à voir avec left, c'est plutôt la différence entre réduire et plier
  • La différence n’est pas l’implémentation, il suffit de regarder les signatures.
  • La question n'a rien à voir avec Scala en particulier, il s'agit plutôt des deux concepts de la programmation fonctionnelle.

Retour à votre question:

Voici la signature de foldLeft (elle aurait pu aussi être foldRight pour le point que je vais faire valoir):

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

Et voici la signature de reduceLeft (là encore, la direction n'a pas d'importance)

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

Ces deux semblent très similaires et ont donc causé la confusion. reduceLeft est un cas particulier de foldLeft (ce qui veut dire que vous parfois pouvez exprimer la même chose en en utilisant l'un d'entre eux).

Lorsque vous appelez reduceLeft dites sur un List[Int] il réduira littéralement la liste entière des entiers en une valeur unique, qui sera de type Int (ou un sur-type de Int, d'où [B >: A]).

Lorsque vous appelez foldLeft dites sur un List[Int] _ il pliera la liste entière (imaginez rouler un morceau de papier) en une seule valeur, mais cette valeur ne doit pas nécessairement être liée à Int (d'où [B]).

Voici un exemple:

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

Cette méthode prend un List[Int] et retourne un Tuple2[List[Int], Int] ou (List[Int] -> Int). Il calcule la somme et retourne un tuple avec une liste d'entiers et sa somme. En passant, la liste est retournée à l'envers, car nous avons utilisé foldLeft au lieu de foldRight.

Regardez n pli pour les gouverner tous pour une explication plus détaillée.

287
agilesteel

reduceLeft n'est qu'une méthode pratique. C'est équivalent à

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

foldLeft est plus générique, vous pouvez l'utiliser pour produire quelque chose de complètement différent de ce que vous avez inséré à l'origine. Alors que reduceLeft ne peut produire qu'un résultat final du même type ou super type du type de collection . Par exemple:

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

Le foldLeft appliquera la fermeture avec le dernier résultat plié (première utilisation de la valeur initiale) et la valeur suivante.

reduceLeft quant à lui, combinera d’abord deux valeurs de la liste et les appliquera à la fermeture. Ensuite, il combinera le reste des valeurs avec le résultat cumulatif. Voir:

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

Si la liste est vide, foldLeft peut présenter la valeur initiale comme un résultat légal. reduceLeft d'autre part n'a pas de valeur légale s'il ne peut pas trouver au moins une valeur dans la liste.

43
thoredge

Pour référence, reduceLeft affichera une erreur s'il est appliqué à un conteneur vide avec l'erreur suivante.

Java.lang.UnsupportedOperationException: empty.reduceLeft

Retravailler le code à utiliser

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

est une option potentielle. Une autre consiste à utiliser la variante reduceLeftOption qui renvoie un résultat encapsulé Option.

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

La raison fondamentale pour laquelle ils sont tous deux dans la bibliothèque standard Scala est probablement parce qu'ils sont tous deux dans la bibliothèque standard Haskell (appelés foldl et foldl1). Si reduceLeft ne l’était pas, il serait assez souvent défini comme une méthode pratique dans différents projets.

5
Alexey Romanov

De Principes de programmation fonctionnelle dans Scala (Martin Odersky):

La fonction reduceLeft est définie comme une fonction plus générale, foldLeft.

foldLeft ressemble à reduceLeft mais prend un accumulateur z, en tant que paramètre supplémentaire, qui est retourné quand foldLeft est appelé sur une liste vide:

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

[par opposition à reduceLeft, qui lève une exception lorsqu'il est appelé sur une liste vide.]

Le cours (voir le chapitre 5.5) fournit des définitions abstraites de ces fonctions, qui illustrent leurs différences, même si leur utilisation de correspondance de modèle et de récursivité est très similaire.

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)
  }
}

Notez que foldLeft renvoie une valeur de type U, qui n'est pas nécessairement du même type que List[T], mais reductionLeft renvoie une valeur du même type que la liste).

2
C8H10N4O2

Pour vraiment comprendre ce que vous faites avec fold/réduire, vérifiez ceci: http://wiki.tcl.tk/1798 très bonne explication. Une fois que vous avez compris le concept de fold, reduction vient avec la réponse ci-dessus: list.tail.foldLeft (list.head) (_)

0
Henry H.