I know that Scala's Lists have a map implementation with signature (f: (A) => B):List[B]
and a foreach implementation with signature (f: (A) => Unit):Unit
but I'm looking for something that accepts m开发者_如何学Goultiple iterables the same way that the Python map accepts multiple iterables.
I'm looking for something with a signature of (f: (A,B) => C, Iterable[A], Iterable[B] ):Iterable[C]
or equivalent. Is there a library where this exists or a comparable way of doing similar?
Edit:
As suggested below I could do
val output = myList zip( otherList ) map( x => x(0) + x(1) )
but that creates a temporary list in between steps. If the commentor would post I could upvote him (hint, hint) but is there another way?
In scala 2.8, there is a method called zipped in Tuple2 & Tuple3 which avoid to create temporary collection. Here is some sample use case:
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>
There is a zip method in both Tuple2 and Tuple3. xs.zip(ys) is the same as (xs,ys).zip
Note: There is also some shortage in (xs,ys).zip and (xs,ys).zipped, make sure that xs can't be a INFINITE Stream. Go to Ticket #2634 for more information. I have a post in nabble.com some days ago which shows my opinions about how to fix this ticket.
The function you're looking for is usually called zipWith
. It's unfortunately not provided in the standard libraries, but it's pretty easy to write:
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
}
This will traverse only once, since the implementations for zip
and map
on iterators are fully lazy.
But why stop at Iterable
? This has an even more general form. We could declare an interface for all data structures that can be zipped this way.
trait Zip[F[_]] {
def zipWith[A,B,C](f: (A,B) => C, a: F[A], b: F[B]): F[C]
}
For example, we can zip functions:
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))
}
There turns out to be an even more general expression of this type. In general, type constructors that allow an implementation of this interface are 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]
}
An implementation of zipWith is then just this:
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)
This generalises to functions of any arity:
m.ap(m.ap(m.ap(m.map(f,a), b), c), d)
The Scalaz library provides Applicative instances for a lot of data structures in the standard library. Also, convenient syntax is provided for ap
. In Scalaz, this function is called <*>
:
def zipWith[F[_]:Applicative,A,B,C](f: A => B => C, a: F[A], b: F[B]) =
(a map f) <*> b
There is a method map2
in the List
object in Scala 2.7 (and 2.8, but it's deprecated in favor of zipped
). You use it like so:
List.map2( List(1,2,3) , List(4,5,6) ) { _ * _ } // Gives List(4,10,18)
Eastsun's already shown how to use zipped
in 2.8 (which works on all collections, not just lists).
Well, I don't know the syntax (f: (A,B) => C, Iterable[A], Iterable[B] ):Iterable[C]
(and I know nothing of Scala), but if I had to guess, it would mean "A function f taking two iterable arguments A and B and returning an iterable C". I'm not sure if this implies that all iterables yield the same number of items.
In Python, I think you're looking for the zip function:
>>> 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 output is only as long as the shortest iterable:
>>> A=range(10, 12)
>>> zip(A, B)
[(10, 1000), (11, 1100)]
Anyway, some built-in Python functions everyone needs to know but easily misses: enumerate
, map
, reduce
, and zip
. filter
used to be on that list, but it's clearer and more flexible to use a list comprehension these days.
精彩评论