Friday, September 18, 2009

A quick sample of Lists and Tuples in Scala

So I've been challenged with a simple problem to solve in Scala, that is; to take a List of values and and then sum each corresponding value in the list together into a resultant list.

For example:

[1,2,3,4,5] becomes [3,5,7,9]

You can accomplish this in Scala using the List class and its functions, these include:
  • zip : this function takes an input list and creates a list of tuples with each mapping pair, discarding jagged values.
  • tail : this function returns a list minus the first element of the list on which tail is performed.
  • foldLeft : combines the elements of the list together with a seed value (in my case I use a target List).
  • reverse : reverses the order of the list.
so here is the code to do this:
object SumPairs extends Application {

val numbers = List(1,2,3,4,5);
val result = numbers.zip(numbers.tail)
.foldLeft(List[Int]()) (
(list, y) => {
y._1 + y._2 :: list
}
)
.reverse;
println(result);

}

Basically this works as follows:
  1. I create a List of Tuples of each adjacent value in the List by getting the tail of the List.
  2. I get each Tuple in the list, sum it and prepend it to the List returned by "foldLeft".
  3. I reverse the List to fix the order as a result of doing a prepend.
Now this is is designed to be functional but not overly complex (at least I hope not), Since I'm still fairly green in the Scala dark arts I'd like to hear on improvements.

BTW the challenge I'm talking about can be found here.

UPDATE:

Another solution would be to use List.flatMap which basically takes a function which returns an Iterable and appends it to a function, it pretty similar, It's maybe a little more concise, I'll let you decide:

val numbers = List(1,2,3,4,5);
numbers.zip(numbers.tail).flatMap {
value => {
List(value._1+value._2);
}
};

4 comments:

Eastsun said...

scala> val nums = 1::2::3::4::5::Nil
nums: List[Int] = List(1, 2, 3, 4, 5)

scala> nums.zip(nums.tail).map{ case(x,y) => x+y }
res0: List[Int] = List(3, 5, 7, 9)

scala>

Keep running said...

scala> val nums = List.range(1,6)
nums: List[Int] = List(1, 2, 3, 4, 5)

scala> for((x,y) <- nums zip nums.tail) yield x+y
res0: List[Int] = List(3, 5, 7, 9)

Unknown said...

Or just map
numbers.zip(numbers.tail).map {
case (a,b) => a + b
}

Tony Morris said...

Use foldRight instead of foldLeft+reverse. Note that reverse is a specialisation of foldLeft and that any foldRight can be written using 2 foldLefts.

foldRight really shines when used across a lazy data structure (though scala.Stream's foldRight is broken).