/* CS:2820 Object Oriented Software Development Spring 2015 The University of Iowa Instructor: Cesare Tinelli */ /* Scala examples seen in class */ /* static scoping rules */ val a = 10 def f (x:Int) = a + x f(4) // *new* immutable variable a, distinct from the previous one val a = 100 // the new a shadows the old one a // however, the old one is unchanged f(4) // blocks can contain both expression *and* (variable/method) definitions // variable k1 is visible only in the block in which it is declared {val k1 = 4 k1*k1 } k1 // error // within {val x = 4 val x = 5 x } // local declarations shadow less local ones val k = 1 {val k = 2; {val k = 3 println(k) } println(k) } println(k) // there is no shadowing within the same block though {val x = 4 val x = 5 // will generate an error x } // method definitions can have local scope too {def h(x:Int) = x + 1 h(9) } h(9) // error // this implies that methods can be defined locally // within other methods def f (x:Int) = { def square (x:Int) = x * x def double (x:Int) = x + x square(x) - double(x) } // Scala has while "statements" too. // As in the case of if, they are actually expressions of type Unit. // While can be seen a mixfix operator with two arguments: // // while (_) _ // // the first argument takes an expression of type bool, // the second takes any expression var j = 10 while (j > 0) j = j - 1 val u = while (j > 0) j = j - 1 // the second argument of while can be a block j = 6 while (j > 0) { println(j) j = j - 1 } // a block of two expressions, both of type Unit { j = 8 while (j > 0) { println(j) j = j - 1 } } // The body of a method can be a block def print_range(m:Int, n:Int) = { var i = m println() while (i <= n) { print(i + " ") i = i + 1 } println() } print_range(4,9) // imperative-style definition of factorial function def fact (n:Int) = { var i = n var f = 1 while (i > 0) { f = f * i i = i - 1 } f } // functional style, recursive definition of factorial def rfact (n:Int) = if (n <= 1) 1 else n * rfact(n-1) // declaring the return type of recursive methods is mandatory def rfact (n:Int):Int = { if (n <= 1) 1 else n * rfact(n-1) } /* Pattern matching */ val t = (3,5,2) // declare three new variables x1,x2,x3; // the value of xi is the value of t's i-th component; // the pattern (x1,x2,x3) is matched against (3,5,2) val (x1,x2,x3) = t // _ is for "don't care" positions val (y1,y2,_) = t // pattern matching can be used with nested patterns val t = ((2,3),5,(6,2)) val (_, x, _) = t val (_, _, (x,_)) = t // patterns are extremely useful with the match construct: // // (_ match {case _ => _ ... case _ => _}) // // the first argument of match is match agains the pattern given // between case and => ; // patterns are checked one at a time, top-down; // the value of the whole match construct is the value of the expression // on the right of the matching pattern var n = 1 n match { case 0 => "zero" case 1 => "one" case _ => "other" } // All right-hand sides of a 'case' clause must be of the same type // if all cases fail an exception is raised n match { case 0 => "zero" } // match and patterns allow one to write cleaner and more compact code def convert (n:Int) = n match { case 0 => "zero" case 1 => "one" case _ => "other" } // the bodies of the case statements can be any expression (in particular a block) // but, like in the the two branches of the if construct, they all have to be of // same type def p (n: Int) = n match { case 0 => { print("You entered ") println("zero") } // type of this is Unit case _ => { print("You did not enter ") println("zero") } // type of this is Unit } // patterns can be complex and nested var v = (1, 2) v match { case (0, 0) => "a" case (0, _) => "b" case (_, 0) => "c" } // bothZero returns "OK" if both of its arguments are 0 // Otherwise it returns "NotOK" // common (bad) coding style def bothZero (x1:Int, x2:Int) = { if (x1 == 0) { if (x2 == 0) "OK" else "NotOK" } else "NotOK" } // same method but using match def bothZero (x1:Int, x2:Int) = (x1, x2) match { case (0, 0) => "OK" case _ => "NotOK" } // the input here is already a pair that can be pattern matched // directly def bz (p:(Int, Int)) = p match { case (0, 0) => true case _ => false } def and (p:(Boolean, Boolean)) = p match { case (true, y) => y case (false, _) => false } // the scope of a pattern variable in a case is the body of that case def and (p:(Boolean, Boolean)) = p match { case (true, y) => y case (false, _) => y // y is undefined here } // factorial with match def fact (n:Int):Int = n match { case 0 => 1 case _ => n * fact(n - 1) } // you can match several values at the same time by putting them into a tuple (0 < 1, 1 == 1) match { case (true, y) => y case (false, _) => false } // the match construct too builds expressions val r = (0 < 1, 1 == 1) match { case (true, y) => y case (false, _) => false }