Domanda

Supponiamo che io sono

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

e desidero produrre un ss dove il baz (i) = foo (i) + bar (i). Un modo posso pensare di fare questo è

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

Tuttavia, questo si sente sia brutto e inefficiente - devo convertire sia seguenti del regolamento provvisorio per le liste (che esplode con le liste pigri), creare questa lista provvisoria di tuple, solo per mappare su di esso e lascia che sia GCed. Forse flussi risolvere il problema pigro, ma in ogni caso, questo si sente come inutilmente brutto. In Lisp, la funzione di carta sarebbe mappa su più sequenze. Vorrei scrivere

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

E non ci sono elenchi provvisori otterrebbero creato da nessuna parte. C'è una funzione mappa-over-Multiple-liste a Scala, o si combina con zip destrutturazione davvero il modo 'giusto' per fare questo?

È stato utile?

Soluzione

La funzione che si desidera si chiama zipWith, ma non è una parte della libreria standard. Sarà a 2.8. (UPDATE: non pare, vedere i commenti)

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

questo biglietto Trac.

Altri suggerimenti

In Scala 2.8:

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

E funziona per più di due operandi nello stesso modo. Cioè si potrebbe poi seguire questo con:

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

Bene, che, la mancanza di zip, è una carenza di Scala 2.7 Seq. Scala 2.8 ha un design collezione ben pensato, per sostituire il modo ad-hoc le collezioni presenti in 2.7 è venuto per essere (si noti che non sono stati tutti creati in una volta, con un disegno unitario).

Ora, quando si vuole evitare la creazione di raccolta temporanea, si dovrebbe usare "proiezione" su Scala 2.7, o "vista" sulla Scala 2.8. Questo vi darà un tipo di raccolta per la quale determinate istruzioni, in particolare carta, flatMap e filtro, non sono severe. Su Scala 2.7, la proiezione di una lista è un flusso. Su Scala 2.8, c'è una SequenceView di una sequenza, ma c'è una zipWith proprio lì in sequenza, non si sarebbe nemmeno bisogno.

Detto questo, come detto, JVM è ottimizzato per gestire le allocazioni di oggetti temporanei, e, durante l'esecuzione in modalità server, l'ottimizzazione in fase di esecuzione può fare miracoli. Quindi, non ottimizzare prematuramente. Testare il codice nelle condizioni sarà gestito - e se non avete pianificato per l'esecuzione in modalità server, quindi ripensare che se il codice dovrebbe essere a lungo in esecuzione, e optmize quando / dove / se necessario <. / p>

Modifica

Quello che in realtà sta per essere disponibile su Scala 2.8 è questa:

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

Un elenco pigro non è una copia di un elenco - è più come un singolo oggetto. Nel caso di un'implementazione zip pigro, ogni volta che viene chiesto per la voce successiva, afferra un elemento da ciascuna delle due liste di input e crea una tupla da loro, e poi rompere la tupla parte con il pattern-matching in il vostro lambda.

Quindi non c'è mai la necessità di creare una copia completa della lista tutta di input (s) prima di iniziare ad operare su di essi. Si riduce a un modello di allocazione molto simile a qualsiasi applicazione in esecuzione sulla JVM -. Un sacco di allocazioni molto breve, ma di piccole dimensioni, che la JVM è ottimizzato per affrontare

Aggiornamento: per essere chiari, è necessario utilizzare Streams (liste pigri) non liste. flussi di Scala hanno una zip che funziona il modo pigro, e quindi non dovrebbero essere convertendo le cose in liste.

Idealmente il vostro algoritmo dovrebbe essere in grado di lavorare su due infinito flussi senza far saltare in aria (ammesso che non fa alcuna folding, naturalmente, ma solo legge e genera flussi).

UPDATE: E 'stato fatto notare (nei commenti) che questa "risposta" in realtà non affronta la domanda che si pone. Questa risposta sarà mappare su ogni combinazione di foo e bar, producendo N x M elementi, invece del min (M, N) come richiesto . Quindi, questo è sbagliato , ma ha lasciato ai posteri dal momento che è una buona informazione.


Il modo migliore per farlo è con flatMap combinato con map. Codice parla più forte delle parole:

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

Questo produrrà un unico Seq[Double], esattamente come ci si aspetterebbe. Questo modello è così comune che Scala in realtà comprende una certa magia sintattica che implementa:

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

O, in alternativa:

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

La sintassi for { ... } è davvero il modo più idiomatico per fare questo. È possibile continuare ad aggiungere clausole del generatore (ad esempio b <- bar) se necessario. Così, se diventa improvvisamente tre Seqs che è necessario mappare sopra, si può facilmente scalare la sintassi con i vostri requisiti (a coniare una frase).

Quando di fronte un compito simile, ho aggiunto il seguente magnaccia a 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
    }
  }
}

Avendo questo, si può fare qualcosa di simile:

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

Si noti che si deve considerare attentamente il tipo di raccolta passato come Iterable annidati, dal momento che tail e head sarà ricorrentemente chiamato su di esso. Quindi, idealmente si dovrebbe passare Iterable[List] o altra raccolta con tail e head veloce.

Inoltre, questo codice si aspetta collezioni annidate della stessa dimensione. Quello era il mio caso d'uso, ma ho il sospetto che questo può essere migliorato, se necessario.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top