Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

zipWith (mapping over multiple Seq) in Scala

Suppose I have

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

and I wish to produce a seq where the baz(i) = foo(i) + bar(i). One way I can think of to do this is

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

However, this feels both ugly and inefficient -- I have to convert both seqs to lists (which explodes with lazy lists), create this temporary list of tuples, only to map over it and let it be GCed. Maybe streams solve the lazy problem, but in any case, this feels like unnecessarily ugly. In lisp, the map function would map over multiple sequences. I would write

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

And no temporary lists would get created anywhere. Is there a map-over-multiple-lists function in Scala, or is zip combined with destructuring really the 'right' way to do this?

like image 988
bsdfish Avatar asked Jul 21 '09 06:07

bsdfish


3 Answers

In Scala 2.8:

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

And it works for more than two operands in the same way. I.e. you could then follow this up with:

(foo, bar, baz).zipped map (_ * _ * _)
like image 179
Martin Odersky Avatar answered Nov 16 '22 23:11

Martin Odersky


The function you want is called zipWith, but it isn't a part of the standard library. It will be in 2.8 (UPDATE: Apparently not, see comments).

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

See this Trac ticket.

like image 15
Alexey Romanov Avatar answered Nov 16 '22 22:11

Alexey Romanov


Well, that, the lack of zip, is a deficiency in Scala's 2.7 Seq. Scala 2.8 has a well-thought collection design, to replace the ad-hoc way the collections present in 2.7 came to be (note that they weren't all created at once, with an unified design).

Now, when you want to avoid creating temporary collection, you should use "projection" on Scala 2.7, or "view" on Scala 2.8. This will give you a collection type for which certain instructions, particularly map, flatMap and filter, are non-strict. On Scala 2.7, the projection of a List is a Stream. On Scala 2.8, there is a SequenceView of a Sequence, but there is a zipWith right there in the Sequence, you wouldn't even need it.

Having said that, as mentioned, JVM is optimized to handle temporary object allocations, and, when running in server mode, the run-time optimization can do wonders. So, do not optimize prematurely. Test the code in the conditions it will be run -- and if you haven't planned to run it in server mode, then rethink that if the code is expected to be long-running, and optmize when/where/if necessary.

EDIT

What is actually going to be available on Scala 2.8 is this:

(foo,bar).zipped.map(_+_)
like image 10
Daniel C. Sobral Avatar answered Nov 17 '22 00:11

Daniel C. Sobral