2010-04-16 6 views
9

So che Le liste di Scala hanno un'implementazione map con la firma (f: (A) => B):List[B] e un'implementazione foreach con la firma (f: (A) => Unit):Unit ma sto cercando qualcosa che accetta più iterables allo stesso modo in cui il Python map accetta più iterabili.Esiste un equivalente in Scala alla funzione di mappa più generale di Python?

Sto cercando qualcosa con una firma di o equivalente. Esiste una biblioteca in cui esiste questo o un modo simile di fare simili?

Edit:

Come suggerito di seguito ho potuto fare

val output = myList zip(otherList) map(x => x(0) + x(1)) 

ma che crea un elenco temporaneo tra i passaggi. Se il commentatore postasse, potrei mandarlo su di lui (suggerimento, suggerimento) ma c'è un altro modo?

+2

Se 'zip()' non funziona, cosa è necessario fare diversamente? –

+0

figlio di un ... zip. Agli ordini! Non ci ho nemmeno pensato. Perché non pubblichi così posso svenderlo. – wheaties

+4

Lascerò @Mike per rispondere, ma puoi fare 'list1 zip list2' e anche' (list1, list2) .zipped'. Quest'ultima, solo Scala 2.8, non crea una collezione temporanea. Inoltre, si può fare 'list1.view zip list2' o' list1.projection zip list2' su Scala 2.8 e 2.7 rispettivamente per evitare di creare collezioni temporanee. –

risposta

12

In scala 2.8, esiste un metodo chiamato zippato in Tuple2 & Tuple3 che evita di creare una raccolta temporanea. Ecco alcuni casi l'uso del campione:

Welcome to Scala version 2.8.0.r21561-b20100414020114 (Java HotSpot(TM) Client VM, Java 1.6.0_18). 
Type in expressions to have them evaluated. 
Type :help for more information. 

scala> val xs = 0 to 9 
xs: scala.collection.immutable.Range.Inclusive with scala.collection.immutable.Range.ByOne = Range(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) 

scala> val ys = List.range(0,10) 
ys: List[Int] = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) 

scala> val zs = Array.range(0,10) 
zs: Array[Int] = Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) 

scala> (xs,ys).zipped.map{ _+_ } 
res1: scala.collection.immutable.IndexedSeq[Int] = Vector(0, 2, 4, 6, 8, 10, 12, 14, 16, 18) 

scala> (zs,ys,xs).zipped.map{ _+_+_ } 
res2: Array[Int] = Array(0, 3, 6, 9, 12, 15, 18, 21, 24, 27) 

scala> 

c'è un metodo zip sia Tuple2 e Tuple3. xs.zip (ys) è la stessa (xs, ys) .zip

Nota: C'è anche una certa carenza di (xs, ys) .zip e (XS, ys) .zipped, accertarsi che xs non può essere un flusso INFINITE. Vai a Ticket #2634 per ulteriori informazioni. Ho a post in nabble.com alcuni giorni fa che mostra le mie opinioni su come risolvere questo ticket.

3

C'è un metodo map2 nell'oggetto List in Scala 2.7 (e 2.8, ma è deprecato a favore di zipped). Si utilizza in questo modo:

List.map2(List(1,2,3) , List(4,5,6)) { _ * _ } // Gives List(4,10,18) 

di Eastsun già mostrato come utilizzare zipped a 2.8 (che funziona su tutte le collezioni, non solo liste).

+0

Apprezzo il commento 2.7. Presto passerò a 2.8 abbastanza presto. – wheaties

2

Beh, non lo so la sintassi (f: (A,B) => C, Iterable[A], Iterable[B]):Iterable[C] (e so nulla della Scala), ma se dovessi indovinare, significherebbe "Una funzione f prendere due argomenti iterabili A e B e restituire un iterable C ". Non sono sicuro che ciò implichi che tutti gli iterabili producano lo stesso numero di elementi.

In Python, penso che stai cercando per la funzione zip:

>>> A = range(10, 15) 
>>> B = range(1000, 1500, 100) 
>>> zip(A, B) 
[(10, 1000), (11, 1100), (12, 1200), (13, 1300), (14, 1400)] 
>>> [a + b for a,b in zip(A, B)] 
[1010, 1111, 1212, 1313, 1414] 

s' zip uscita è solo fino a quando l'iterabile più breve:

>>> A=range(10, 12) 
>>> zip(A, B) 
[(10, 1000), (11, 1100)] 

In ogni caso, un po' incorporato in Python funziona che tutti hanno bisogno di conoscere ma facilmente non riescono a: enumerate, map, reduce e zip.filter si trovava in quella lista, ma in questi giorni è più chiaro e più flessibile usare una comprensione delle liste.

11

La funzione che stai cercando è solitamente denominata zipWith. Non è purtroppo previsto nelle librerie standard, ma è abbastanza facile da scrivere:

def zipWith[A,B,C](f: (A,B) => C, a: Iterable[A], b: Iterable[B]) = 
    new Iterable[C] { 
    def elements = (a.elements zip b.elements) map f.tupled 
    } 

Ciò attraversare solo una volta, dal momento che le implementazioni per zip e map su iteratori sono completamente pigri.

Ma perché fermarsi allo Iterable? Questo ha una forma ancora più generale. Potremmo dichiarare un'interfaccia per tutte le strutture di dati che possono essere compresse in questo modo.

trait Zip[F[_]] { 
    def zipWith[A,B,C](f: (A,B) => C, a: F[A], b: F[B]): F[C] 
} 

Ad esempio, possiamo comprimere funzioni:

trait Reader[A] { 
    type Read[B] = (A => B) 
} 

def readerZip[T] = new Zip[Reader[T]#Read] { 
    def zipWith[A,B,C](f: (A,B) => C, a: T => A, b: T => B): T => C = 
    (t: T) => f(a(t),b(t)) 
} 

Ci risulta essere un'espressione ancora più generale di questo tipo. In generale, costruttori di tipo che consentono un'implementazione di questa interfaccia sono applicative functors

trait Applicative[F[_]] { 
    def pure[A](a: A): F[A] 
    def map[A,B](f: A => B, a: F[A]): F[B] 
    def ap[A,B](f: F[A => B], a: F[A]): F[B] 
} 

Un'implementazione zipWith viene poi solo questo:

def zipWith[F[_],A,B,C](f: A => B => C, a: F[A], b: F[B]) 
         (implicit m: Applicative[F]) = 
    m.ap(m.map(f,a), b) 

Questo generalizza a funzioni di qualsiasi arietà:

m.ap(m.ap(m.ap(m.map(f,a), b), c), d) 

La libreria Scalaz fornisce istanze Applicative per molte strutture di dati nella libreria standard. Inoltre, viene fornita una comoda sintassi per ap. In Scalaz, questa funzione è chiamata <*>:

def zipWith[F[_]:Applicative,A,B,C](f: A => B => C, a: F[A], b: F[B]) = 
    (a map f) <*> b 
+0

Grazie. Ho così tanto da imparare. Questo è stato molto istruttivo. – wheaties