Question

Supposons que j'ai

val foo : Seq[Double] = ...
val bar : Seq[Double] = ...

et je souhaite produire un seq où le baz (i) = foo (i) + bar (i). Une façon je peux penser à faire est

val baz : Seq[Double] = (foo.toList zip bar.toList) map ((f: Double, b : Double) => f+b)

Cependant, cela se sent à la fois laid et inefficace - je dois convertir les deux seqs aux listes (qui explose avec des listes paresseux), créez cette liste temporaire de tuples, pour la carte sur elle et que ce soit GCed. Peut-être que les flux résoudre le problème paresseux, mais en tout cas, cela se sent comme inutilement laid. En Lisp, la fonction de carte mapperait sur plusieurs séquences. Je voudrais écrire

(mapcar (lambda (f b) (+ f b)) foo bar)

Et aucune liste temporaire ne sont créées partout. Y at-il une fonction carte-over-plusieurs listes à Scala, ou est combiné avec zip destructuration vraiment la façon de faire « droit »?

Était-ce utile?

La solution

La fonction que vous voulez est appelé zipWith, mais il ne fait pas partie de la bibliothèque standard. Il sera en 2.8. (MISE À JOUR: Apparemment pas, voir les commentaires)

foo zipWith((f: Double, b : Double) => f+b) bar

Voir ce billet Trac .

Autres conseils

Scala 2.8:

val baz = (foo, bar).zipped map (_ + _)

Et cela fonctionne pour plus de deux opérandes de la même manière. C'est à dire. vous pouvez alors suivre cette question avec:

(foo, bar, baz).zipped map (_ * _ * _)

Eh bien, que le manque de zip, est une carence en 2,7 Seq Scala. Scala 2.8 a un design de collection bien pensé, pour remplacer la façon ad hoc les collections présentes dans 2.7 sont venus à être (noter qu'ils ne sont pas tous créés à la fois, avec une conception unifiée).

Maintenant, quand vous voulez éviter de créer la collection temporaire, vous devez utiliser la « projection » sur Scala 2.7, ou « vue » sur Scala 2.8. Cela vous donnera un type de collection pour laquelle certaines instructions, carte particulièrement, flatMap et filtre, ne sont pas strictes. Le Scala 2.7, la projection d'une liste est un flux. Le Scala 2.8, il y a un SequenceView d'une séquence, mais il y a un zipWith là, dans la séquence, vous ne devez même pas.

Cela dit, comme mentionné, JVM est optimisée pour gérer les allocations d'objet temporaires, et, lors de l'exécution en mode serveur, l'optimisation d'exécution peut faire des merveilles. Donc, ne pas optimiser prématurément. Testez le code dans les conditions qu'il sera exécuté - et si vous ne l'avez pas prévu de l'exécuter en mode serveur, puis repenser que si le code devrait être de longue durée, et lorsque Optimiser l'utilisation / où / si nécessaire <. / p>

EDIT

Qu'est-ce qui se passe réellement à être disponible sur Scala 2.8 est la suivante:

(foo,bar).zipped.map(_+_)

Une liste paresseuse n'est pas une copie d'une liste - il est plus comme un seul objet. Dans le cas d'une mise en œuvre zip paresseux, chaque fois qu'il est demandé que le prochain article, il saisit un élément de chacune des deux listes d'entrée et crée un tuple d'eux, et vous ensuite briser le tuple avec le motif correspondant à votre lambda.

Il n'y a donc jamais besoin de créer une copie complète de toute la liste d'entrée (s) avant de commencer à opérer sur eux. Il se résume à un modèle d'allocation très similaire à toute application en cours d'exécution sur la machine virtuelle Java -. Beaucoup d'allocations de très courte durée, mais les petits, que la machine virtuelle Java est optimisé pour traiter

Mise à jour: pour être clair, vous devez utiliser Streams (listes paresseux) pas de listes. Les flux de Scala ont un zip qui fonctionne comme paresseux, et donc vous ne devriez pas convertiront les choses dans des listes.

Idéalement, votre algorithme doit être capable de travailler sur deux infini les cours d'eau sans faire sauter (en supposant qu'il ne fait pas tout folding, bien sûr, mais lit juste et génère des flux).

Mise à jour: Il a été souligné (dans les commentaires) que cette « réponse » ne traite pas réellement posé la question. Cette réponse carte sur tous les combinaison de foo et bar, production N x M éléments, au lieu de min (M, N) a demandé . Donc, cela est mauvais , mais a laissé pour la postérité car il est une bonne information.


La meilleure façon de le faire est avec flatMap combiné avec map. Code parle plus fort que les mots:

foo flatMap { f => bar map { b => f + b } }

Cela produira une seule Seq[Double], exactement comme on peut s'y attendre. Ce modèle est si commun que Scala comprend en fait un peu de magie syntaxique qui implémente:

for {
  f <- foo
  b <- bar
} yield f + b

Ou bien:

for (f <- foo; b <- bar) yield f + b

La syntaxe de for { ... } est vraiment la façon la plus idiomatiques de le faire. Vous pouvez continuer à ajouter des clauses de générateur (par exemple b <- bar) si nécessaire. Ainsi, si elle devient soudainement trois Seqs que vous devez mapper sur, vous pouvez facilement faire évoluer votre syntaxe ainsi que vos besoins (selon une expression).

fait face à une tâche similaire, j'ai ajouté le proxénète à la suite Iterables:

implicit class IterableOfIterablePimps[T](collOfColls: Iterable[Iterable[T]]) {
  def mapZipped[V](f: Iterable[T] => V): Iterable[V] = new Iterable[V] {
    override def iterator: Iterator[V] = new Iterator[V] {
      override def next(): V = {
        val v = f(itemsLeft.map(_.head))
        itemsLeft = itemsLeft.map(_.tail)
        v
      }

      override def hasNext: Boolean = itemsLeft.exists(_.nonEmpty)

      private var itemsLeft = collOfColls
    }
  }
}

Avoir cela, on peut faire quelque chose comme:

val collOfColls = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))
collOfColls.mapZipped { group =>
  group // List(1, 4, 7), then List(2, 5, 8), then List(3, 6, 9)
}

Notez que vous devez examiner soigneusement le type de collection passé comme Iterable imbriquées, depuis tail et head sera appelé là-dessus de façon récurrente. Donc, idéalement, vous devriez passer Iterable[List] ou autre collection avec tail rapide et head.

En outre, ce code prévoit des collections imbriquées de la même taille. Ce fut mon cas d'utilisation, mais je soupçonne que cela peut être amélioré, si nécessaire.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top