2010-04-16 12 views
9

sé que las listas de Scala tienen una aplicación con la firma map(f: (A) => B):List[B] y una aplicación con la firma foreach(f: (A) => Unit):Unit pero estoy buscando algo que acepta múltiples iterables de la misma manera que el pitón map acepta múltiples iterables.¿Existe un equivalente en Scala a la función de mapa más general de Python?

Estoy buscando algo con una firma de (f: (A,B) => C, Iterable[A], Iterable[B]):Iterable[C] o equivalente. ¿Existe una biblioteca donde existe o una forma similar de hacer algo similar?

Editar:

Como se sugiere a continuación que podía hacer

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

sino que crea una lista temporal entre los pasos. Si el comentarista publicara, podría volver a votarlo (sugerencia, sugerencia) pero, ¿hay alguna otra manera?

+2

Si 'postal()' no funciona, ¿qué necesita hacerse de otra manera? –

+0

hijo de ... zip. ¡Righto! Ni siquiera pensé en eso. ¿Por qué no publicas para poder votarlo? – wheaties

+4

Dejaré @Mike para responder, pero puedes hacer 'list1 zip list2' y también' (list1, list2) .zipped'. El último, Scala 2.8 solamente, no crea una colección temporal. Además, puede hacer 'list1.view zip list2' o' list1.projection zip list2' en Scala 2.8 y 2.7 respectivamente para evitar crear colecciones temporales. –

Respuesta

12

En scala 2.8, hay un método llamado comprimido en Tuple2 & Tuple3 que evita crear una colección temporal. Aquí es un poco de caso de uso de ejemplo:

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> 

hay un método postal en tanto Tuple2 y Tuple3. xs.zip (YS) es lo mismo que (xs, ys) .zip

Nota: Hay también una cierta escasez de (xs, ys) .zip y (xs, ys) .zipped, asegúrese ese xs no puede ser un flujo INFINITO. Vaya a Ticket #2634 para obtener más información. Tengo a post en nabble.com hace unos días que muestra mis opiniones sobre cómo solucionar este problema.

3

Hay un método map2 en el objeto List en Scala 2.7 (y 2.8, pero está obsoleto a favor de zipped). Lo usa de esta manera:

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

de Eastsun que se muestran cómo utilizar zipped en 2.8 (que funciona en todas las colecciones, no sólo enumera).

+0

Aprecio el comentario 2.7. Pronto estaré en transición a 2.8. – wheaties

2

Bueno, yo no sé la sintaxis (f: (A,B) => C, Iterable[A], Iterable[B]):Iterable[C] (y sé nada de Scala), pero si tuviera que adivinar, que significaría "Una función f tomar dos argumentos iterables Un y B y devolver un iterable C ". No estoy seguro si esto implica que todos los iterables producen la misma cantidad de elementos.

En Python, creo que estés buscando la función 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] 

zip 's de salida es sólo el tiempo que el menor iterable:

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

De todos modos, algunos incorporado en las funciones de Python, todos deben saberlo, pero se pierden fácilmente: enumerate, map, reduce y zip.filter solía estar en esa lista, pero es más claro y más flexible utilizar una lista de comprensión en estos días.

11

La función que está buscando generalmente se llama zipWith. Lamentablemente no está previsto en las bibliotecas estándar, pero es bastante fácil de escribir:

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 
    } 

Este atravesará una sola vez, ya que las implementaciones para zip y map sobre iteradores son totalmente perezoso.

¿Pero por qué detenerse en Iterable? Esto tiene una forma aún más general. Podríamos declarar una interfaz para todas las estructuras de datos que pueden ser comprimidas de esta manera.

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

Por ejemplo, podemos comprimir funciones:

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

No resulta ser una expresión más general de este tipo. En general, los constructores de tipos generales que permiten una implementación de esta interfaz son 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] 
} 

Una implementación de zipWith es entonces simplemente esto:

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) 

Esto generaliza a funciones de cualquier aridad:

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

La biblioteca Scalaz proporciona instancias aplicables para muchas estructuras de datos en la biblioteca estándar. Además, se proporciona una sintaxis conveniente para ap. En Scalaz, esta función se llama <*>:

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

Gracias. Tengo mucho que aprender Esto fue muy educativo. – wheaties

Cuestiones relacionadas