Showing posts with label for-comprehension. Show all posts
Showing posts with label for-comprehension. Show all posts

Tuesday, February 2, 2010

Blocks within for-comprehensions

In another example of uniformity it is possible to use blocks within a for-comprehension when creating a generator or making an assignment. The basic form of a for-comprehension is
  1. for {i <- generator 
  2.      if guard
  3.      j = assignment } yield result

The generator, guard, assignment and result are all expressions which means they can all be blocks or simple statements. Most commonly you will see them as simple statements:
  1. scala> for {i <- 1 to 10
  2.      |      if i % 2 == 0
  3.      |      j = i } yield j
  4. res50: scala.collection.immutable.IndexedSeq[Int] = IndexedSeq(2, 4, 6, 8, 10)

But since they are expressions they can be more complex:
  1. scala> for {  
  2.      |  i <- { 
  3.      |     val start = nextInt(3)
  4.      |     val end = nextInt(10)+start
  5.      |     start to end
  6.      |  }
  7.      |  if {
  8.      |    val cut = nextInt(3)+1
  9.      |    i % cut == 0
  10.      |  }
  11.      |  j = {
  12.      |    val x = i+1
  13.      |    x / 2
  14.      |  }
  15.      |  }  yield {
  16.      |    // do a debug println
  17.      |    println(j)
  18.      |  j
  19.      |  }
  20. 1
  21. 1
  22. 2
  23. 3
  24. res53: scala.collection.immutable.IndexedSeq[Int] = IndexedSeq(1, 1, 2, 3)

Tuesday, January 26, 2010

Guard Sugar

This topic is a simple tip for a cleaner syntax for guards. Guards/filters are statements in for-comprehensions and case statements that guard or filter matches. Often you will see them as follows:
  1. scala> for (i <- 1 to 10; if (i % 2 == 1)) yield i
  2. res0: scala.collection.immutable.IndexedSeq[Int] = IndexedSeq(1, 3, 5, 7, 9)
  3. scala> util.Random.nextInt(10) match {
  4.      | case i if(i>5) => println("hi")
  5.      | case _ => println("low")
  6.      | }
  7. low

However you have the option to apply a little sugar to the statements cleaning them up a little:
  1. scala> for (i <- 1 to 10; if i % 2 == 1) yield i  
  2. res2: scala.collection.immutable.IndexedSeq[Int] = IndexedSeq(1, 3, 5, 7, 9)
  3. scala> util.Random.nextInt(10) match {          
  4.      | case i if i>5 => println("hi")           
  5.      | case _ => println("low")                 
  6.      | }
  7. hi

As you can see the brackets are optional. That is because in both cases the brackets are not required for the parser to determine the start and end of the guard statements. They are added so that the "normal" syntax used in the typical if statements will compile.
  1. scala> 10 match { case i if i == 1 || i == 10 => "obviously this is a match"
  2. res4: java.lang.String = obviously this is a match
  3. /*
  4. In case statements you can split the if almost any way you want because it is very clearly bound by the if and the => that is required for all case statements with a guard
  5. */
  6. scala> 10 match { case i if i == 1 ||                                        
  7.      | i == 10 => "obviously this is a match"}
  8. res5: java.lang.String = obviously this is a match
  9. scala> 10 match {        
  10.      | case i
  11.      | if
  12.      | i == 1
  13.      | ||
  14.      | i == 10 
  15.      | => "matched"
  16.      | }
  17. res6: java.lang.String = matched
  18. /*
  19. For-comprehensions are not as flexible since it is harder for the compiler to determine where the guard ends. So try to keep it on one line or otherwise make it a function.  That is probably good advice for case statements as well.
  20. */
  21. scala> for { 
  22.      | x <- 1 to 10
  23.      | if
  24.      | x > 10
  25.      | || x == 2 } yield x
  26. < console>:5: error: '<-' expected but integer literal found.
  27.        || x == 2 } yield x
  28.                ^
  29. < console>:5: error: illegal start of simple expression
  30.        || x == 2 } yield x
  31.                  ^
  32. scala> for {              
  33.      | x <- 1 to 10       
  34.      | if x == 1 || x == 10 } yield x
  35. res7: scala.collection.immutable.IndexedSeq[Int] = IndexedSeq(1, 10)

Wednesday, January 6, 2010

Matching in for-comprehensions

At a glance a for-comprehension appears to be equivalent to a Java for-loop, but it is much much more than that. As shown in post: for-comprehensions, for-comprehensions can have guards which filter out which elements are processed:
  1. scala> for ( x <- 1 to 10; if (x >4) ) println(x)
  2. 5
  3. 6
  4. 7
  5. 8
  6. 9
  7. 10

They can be used to construct new collections:
  1. scala>?for(?i?<-?List(?"a",?"b",?"c")?)?yield?"Word:?"+i
  2. res1:?List[java.lang.String]?=?List(Word:?a,?Word:?b,?Word:?c)

They can contain multiple generators:
  1. scala> for {x <- 1 to 10                           
  2.      |      if(x%2 == 0)
  3.      |      y <- 1 to 5} yield (x,y)
  4. res1: scala.collection.immutable.IndexedSeq[(Int, Int)] = IndexedSeq((2,1), (2,2), (2,3), (2,4), (2,5), (4,1), (4,2), (4,3), (4,4), (4,5), (6,1), (6,2), (6,3), (6,4), (6,5), (8,1), (8,2), (8,3), (8,4), (8,5), (10,1), (10,2), (10,3), (10,4), (10,5))

What has not been covered is that the assignments also does pattern matching:
  1. scala> for ( (x,y) <- (6 to 1 by -2).zipWithIndex) println (x,y) 
  2. (6,0)
  3. (4,1)
  4. (2,2)

This is not surprising as this also occurs during normal assignment. But what is interesting is that the pattern matching can act as a guard as well. See Extractor examples and Assignment and Parameter Objects for more information of pattern matching and extractors.
  1. scala> val args = Array( "h=2""b=3")
  2. args: Array[java.lang.String] = Array(h=2, b=3)
  3. scala> val Property = """(.+)=(.+)""".r 
  4. Property: scala.util.matching.Regex = (.+)=(.+)
  5. scala> for {Property(key,value) <- args } yield (key,value)
  6. res0: Array[(String, String)] = Array((h,2), (b,3))
  7. scala> Map(res0:_*)
  8. res1: scala.collection.immutable.Map[String,String] = Map(h -> 2, b -> 3)
  9. scala> res1("h")
  10. res3: String = 2

Now just for fun here is a similar example but using symbols instead of strings for the key values:
  1. scala> val args = Array( "h=2""b=3")
  2. args: Array[java.lang.String] = Array(h=2, b=3)
  3. scala> val Property = """(.+)=(.+)""".r 
  4. Property: scala.util.matching.Regex = (.+)=(.+)
  5. scala> for {Property(key,value) <- args } yield (Symbol(key),value)
  6. res0: Array[(Symbol, String)] = Array(('h,2), ('b,3))
  7. scala> Map(res0:_*)
  8. res1: scala.collection.immutable.Map[Symbol,String] = Map('h -> 2, 'b -> 3)
  9. scala> res1('h)
  10. res2: String = 2

Tuesday, August 11, 2009

For-comprehensions

The for-comprehension construct is a very powerful way of iterating over collections. In its most basic form it is the java for( var: collection){} loop. As with all flow constructs in Scala, the scala for loop (or more correctly for-comprehension) can return a value. In the case of the for-comprehension it returns Unit (similar to void in Java terms) or a Sequence if yield is used.
  1. scala>val range = 1 to 5
  2. range: Range.Inclusive = Range(1, 2, 3, 4, 5)
  3. // no return value if there is no 'yield' keyword
  4. scala>for( i <- 1 to 10 ) { i + 1 }
  5. // if there is a yield a collection is returned
  6. // the type of collection depends on the input
  7. // here a Range is returned
  8. scala>for( i <- range ) yield i+1
  9. res1: RandomAccessSeq.Projection[Int] = RangeM(2, 3, 4, 5, 6)
  10. // here a list is returned
  11. scala>for( i <- list( "a", "b", "c") ) yield"Word: "+i
  12. res1: List[java.lang.String] = List(Word: a, Word: b, Word: c)
  13. // you can filter the elements that visited in the loop
  14. scala>for( i <- range; if( i % 2 == 0) ) yield i
  15. res2: Seq.Projection[Int] = RangeFM(2, 4)
  16. // this    is more    about creating ranges than loops
  17. scala>for ( i <- 20 until (10,-2) ) yield i
  18. res3: RandomAccessSeq.Projection[Int] = RangeM(20, 18, 16, 14, 12)
  19. // you can string together multiple "generators"
  20. scala>for( i <- range; j <- range) yield (i,j)
  21. res4: Seq.Projection[(Int, Int)] = RangeG((1,1), (1,2), (1,3), (1,4), (1,5), (2,1), (2,2), (2,3), (2,4), (2,5), (3,1), (3,2), (3,3), (3,4), (3,5), (4,1), (4,2), (4,3), (4,4), (4,5), (5,1), (5,2), (5,3), (5\
  22. ,4), (5,5))
  23. // you can also    declar variables as part of the    loop declaration
  24. scala>for( i <- range; j <- 1 to i; k = i-j) yield k
  25. res5: Seq.Projection[Int] = RangeG(0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 0)
  26. // with round brackets '(' and ')' multiple lines will require semi-colons
  27. scala>for (
  28.      | i <- range;
  29.      | j <- 1 to i;
  30.      | k = i-j) yield k
  31. res6: Seq.Projection[Int] = RangeG(0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 0)
  32. // with curly brackets '{' and '}' multiple lines you do not require semi-colons
  33. scala>for {
  34.      | i <- range
  35.      | j <- 1 to i
  36.      | k = i-j}
  37.      | yield{
  38.      | k
  39.      | }
  40. res7: Seq.Projection[Int] = RangeG(0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 0)
  41. scala>for( i <- "enjoy" ) print(i)
  42. enjoy

Friday, August 7, 2009

Creating XML

Scala allows you to embed XML directly into a program and provides several ways to manipulate it. Today we will look at writing XML.
  1. scala>val xml = <root>
  2.      | <child>text</child>
  3.      | </root>
  4. xml: scala.xml.Elem =
  5. <root>
  6.        <child>text</child>
  7.        </root>
  8. scala>val data = "a string"
  9. data: java.lang.String = a string
  10. // you can embed logic and variables in the xml by surrounding with {}
  11. scala>val xml = <root>{data}</root>
  12. xml: scala.xml.Elem = <root>a string</root>
  13. scala>val xml = <root>{ for( i <- 1 to 3 ) yield {<xml i={i.toString}/>} } </root>
  14. xml: scala.xml.Elem = <root><xml i="1"></xml><xml i="2"></xml><xml i="3"></xml></root>
  15. scala>val xml = <root>{ for( i <- 1 to 3 ) yield<child>{i}</child> } </root>
  16. xml: scala.xml.Elem = <root><child>1</child><child>2</child><child>3</child></root>
  17. // save xml to file.  Note Scala 2.8 is changin save API
  18. // and will require the encoding and DocType information
  19. scala> scala.xml.XML.save( "/tmp/doc.xml", xml)

Monday, August 3, 2009

Basic Collection Operations

In this example I show some common operations that can be performed on all collections. The examples use list but any Iterable can be used.
  1. scala>val list = List(1,2,3,4)
  2. list: List[Int] = List(1, 2, 3, 4)
  3. scala> list.mkString(",")
  4. res0: String = 1,2,3,4
  5. scala> list.mkString("(",",",")")
  6. res1: String = (1,2,3,4)
  7. scala> list.reduceLeft ( (next,cumulative) => cumulative + next )
  8. res2: Int = 10
  9. scala>for( e <- list ) println ( e ) 
  10. 1 2 3 4  
  11. scala>for( e <- list ) {      
  12.      | println ( e )      
  13.      | } 
  14. 1 2 3 4  
  15. scala>for( e <- list ) yield e+1 
  16. res5: List[Int] = List(2, 3, 4, 5)  
  17. scala>for ( e <- list; if( e % 2 == 0 ) ) yield e 
  18. res6: List[Int] = List(2, 4)
Subscribe to: Comments (Atom)

AltStyle によって変換されたページ (->オリジナル) /