Showing posts with label iterator. Show all posts
Showing posts with label iterator. Show all posts

Thursday, November 12, 2009

Iterator.Sliding

This topic requires Scala 2.8 or higher.

Iterator and Iterable have most of the most useful methods when dealing with collections. Fold, Map, Filter are probably the most common. But other very useful methods include grouped/groupBy, sliding, find, forall, foreach, and many more. I want to cover Iterator's sliding method.

The def sliding[B >: A](size : Int, step : Int) : GroupedIterator[B] method provides a sliding window on the iterator. The first parameter is the size of the window and the second is the number of elements to skip each time the window is moved. If step == size then this method is the same as grouped. Step defaults to one

Examples:
  1. // notice resulting lists are all 3 long and increases by 1
  2. scala> (1 to 5).iterator.sliding(3).toList
  3. res18: List[Sequence[Int]] = List(List(1, 2, 3), List(2, 3, 4), List(3, 4, 5))
  4. // step is 3 so now there are only 2 groups
  5. scala> (1 to 5).iterator.sliding(4, 3).toList
  6. res19: List[Sequence[Int]] = List(List(1, 2, 3, 4), List(4, 5))
  7. // withPartial(false) makes the partial group be ignored
  8. scala> (1 to 5).iterator.sliding(4, 3).withPartial(false).toList
  9. res21: List[Sequence[Int]] = List(List(1, 2, 3, 4))
  10. scala> val it2 = Iterator.iterate(20)(_ + 5)
  11. it2: Iterator[Int] = non-empty iterator
  12. // withPadding fills in the partial group
  13. // notice with padding is by-name (it is executed each time it is called)
  14. scala> (1 to 5).iterator.sliding(4, 3).withPadding(it2.next).toList
  15. res23: List[Sequence[Int]] = List(List(1, 2, 3, 4), List(4, 5, 20, 25))

Tuesday, September 8, 2009

Collection/Iterable/Iterator foldRight and foldLeft

Folding and reducing are two types of operations that are both very important with dealing with collections in Scala (and other functional languages). This topic covers the fold operations. The reduce topic was previously covered.

A simplistic explanation is they allow processing of the collections. I think about foldRight and foldLeft to be very similar to a visitor that visits each element and performs a calculation. It does not change the values of the collection instead it calculates a result from visiting each element. In Java you might see the following pattern:
  1. class Collection {
  2.   pubic Result op( Result startingValue, Op operation){...}
  3. }
  4. interface Op{
  5.   public Result op( Object nextCollectionElem, Result lastCalculatedResult)
  6. }

To a Java programmer this is pretty obvious how you use it. This is essentially the fold operation. The difference is we are using closures so there is almost no boilerplate and all collection/Iteratore/Iterable interfaces have the fold methods.

One last point before examples. For fold and reduce operations there are both foldLeft and foldRight and similarly reduceLeft and reduceRight. These indicate the order that a collection is processed. Note that for certain collections one direction is more performant that the other. Lists, for example, are better to process left to right because of the datastructure used.

Some tasks that are suited to fold are:
  • Find the 5 smallest elements in a list
  • Sum all the values of a Map together
  • Sum the lengths of the Strings contained in the collection

There are a number of ways that fold can be written syntactically. The following examples are ways to sum together the integers of a Map. Note that /: is an alias for foldLeft and :/ is an alias for foldRight. Normally foldLeft and foldRight should be used unless the rest of the developers who will be reading the code are quite confortable reading Scala.

  1. // fold right processes from left to right
  2. scala> val m = Map(1 -> "one", 2 -> "two", 3 -> "three")
  3. m: scala.collection.immutable.Map[Int,java.lang.String] = Map(1 -> one, 2 -> two, 3 -> three)
  4. scala> m.foldRight(0)((kv: (Int, String), a : Int) => a+kv._2.length)
  5. res1: Int = 11
  6. scala> (m :\ 0)((kv: (Int, String), a : Int) => a+kv._2.length)
  7. res5: Int = 11
  8. scala> m.foldRight(0){ case ((key, value), a) => a+value.length}
  9. res7: Int = 11
  10. scala> m.foldRight(0){ case (kv, a) => a+kv._2.length} }}
  11. res8: Int = 11
  12. // fold left processes from right to left (usually slower than foldRight)
  13. scala> val m = Map("a" -> 1, "b" -> 2, "c" -> 3)
  14. m: scala.collection.immutable.Map[java.lang.String,Int] = Map(a -> 1, b -> 2, c -> 3)
  15. scala> m.foldLeft(0)( (accum, kv) => accum + kv._2 )
  16. res9: Int = 6
  17. scala> m.foldLeft(0){case (accum, (key, value)) => accum + value}
  18. res5: Int = 6
  19. scala> (0 /: m){case (accum, (key, value)) => accum + value}
  20. res4: Int = 6