progs/lecture2.scala
changeset 192 a112e0e2325c
parent 174 90e0b1cc460b
child 204 9b45dd24271b
equal deleted inserted replaced
188:937c995b047a 192:a112e0e2325c
     1 // Scala Lecture 2
     1 // Scala Lecture 2
     2 //=================
     2 //=================
     3 
     3 
     4 
     4 
     5 // the pain with overloaded math operations
       
     6 
       
     7 (100 / 4)
       
     8 
       
     9 (100 / 3)
       
    10 
       
    11 (100.toDouble / 3.toDouble)
       
    12 
       
    13 
       
    14 // For-Comprehensions again
       
    15 //==========================
       
    16 
       
    17 def square(n: Int) : Int = n * n
       
    18 
       
    19 for (n <- (1 to 10).toList) yield {
       
    20   val res = square(n)
       
    21   res
       
    22 }
       
    23 
       
    24 // like in functions, the "last" item inside the yield
       
    25 // will be returned; the last item is not necessarily 
       
    26 // the last line
       
    27 
       
    28 for (n <- (1 to 10).toList) yield {
       
    29   if (n % 2 == 0) n 
       
    30   else square(n)
       
    31 }
       
    32 
       
    33 
       
    34 // ...please, please do not write:
       
    35 val lst = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
       
    36 
       
    37 for (i <- (0 until lst.length).toList) yield square(lst(i))
       
    38 
       
    39 // this is just so prone to off-by-one errors;
       
    40 // write instead
       
    41 
       
    42 for (e <- lst; if (e % 2) == 0; if (e != 4)) yield square(e)
       
    43 
       
    44 
       
    45 //this works for sets as well
       
    46 val st = Set(1, 2, 3, 4, 5, 6, 7, 8, 9)
       
    47 
       
    48 for (e <- st) yield {
       
    49   if (e < 5) e else square(e)
       
    50 }
       
    51 
       
    52 
       
    53 
       
    54 // Side-Effects
       
    55 //==============
       
    56 
       
    57 // with only a side-effect (no list is produced),
       
    58 // for has no "yield"
       
    59 
       
    60 for (n <- (1 to 10)) println(n)
       
    61 
       
    62 
       
    63 for (n <- (1 to 10)) {
       
    64   print("The number is: ")
       
    65   print(n)
       
    66   print("\n")
       
    67 }
       
    68 
       
    69 
       
    70 
       
    71 
       
    72 // know when to use yield and when not:
       
    73 
       
    74 val test = 
       
    75  for (e <- Set(1, 2, 3, 4, 5, 6, 7, 8, 9); if e < 5) yield square(e)
       
    76 
       
    77 
       
    78 
       
    79 // Option type
     5 // Option type
    80 //=============
     6 //=============
    81 
     7 
    82 //in Java, if something unusually happens, you return null;
     8 //in Java if something unusually happens, you return null;
    83 //in Scala you use Option
     9 //in Scala you use Option
    84 //   - if the value is present, you use Some(value)
    10 //   - if the value is present, you use Some(value)
    85 //   - if no value is present, you use None
    11 //   - if no value is present, you use None
    86 
    12 
    87 
    13 
    88 List(7,24,3,4,5,6).find(_ < 4)
    14 List(7,2,3,4,5,6).find(_ < 4)
    89 List(5,6,7,8,9).find(_ < 4)
    15 List(5,6,7,8,9).find(_ < 4)
    90 
    16 
    91 List(7,2,3,4,5,6).filter(_ < 4)
    17 
    92 
    18 // Values in types
    93 // some operations on Option's
    19 //
       
    20 // Boolean: 
       
    21 // Int: 
       
    22 // String: 
       
    23 //
       
    24 // Option[String]:
       
    25 //   
       
    26 
    94 
    27 
    95 val lst = List(None, Some(1), Some(2), None, Some(3))
    28 val lst = List(None, Some(1), Some(2), None, Some(3))
    96 
    29 
    97 lst.flatten
    30 lst.flatten
    98 
    31 
    99 Some(10).get
    32 Some(1).get
   100 None.get
       
   101 
    33 
   102 Some(1).isDefined
    34 Some(1).isDefined
   103 None.isDefined
    35 None.isDefined
   104 
    36 
   105 val ps = List((3, 0), (3, 2), (4, 2), (2, 0), (1, 0), (1, 1))
    37 val ps = List((3, 0), (3, 2), (4, 2), (2, 0), (1, 0), (1, 1))
   106 
    38 
   107 for ((x, y) <- ps) yield {
    39 for ((x, y) <- ps) yield {
   108   if (y == 0) None else Some(x / y)
    40   if (y == 0) None else Some(x / y)
   109 }
    41 }
   110 
    42 
   111 // use .getOrElse is for setting a default value
    43 // getOrElse is for setting a default value
   112 
    44 
   113 val lst = List(None, Some(1), Some(2), None, Some(3))
    45 val lst = List(None, Some(1), Some(2), None, Some(3))
   114 
       
   115 for (x <- lst) yield x.getOrElse(0)
    46 for (x <- lst) yield x.getOrElse(0)
   116 
    47 
   117 
    48 
   118 
    49 
   119 
    50 
   120 // error handling with Options (no exceptions)
    51 // error handling with Option (no exceptions)
   121 //
       
   122 //  Try(....)
       
   123 //
    52 //
   124 //  Try(something).getOrElse(what_to_do_in_an_exception)
    53 //  Try(something).getOrElse(what_to_do_in_an_exception)
   125 //
    54 //
   126 import scala.util._
    55 import scala.util._
   127 
       
   128 Try(1 + 3)
       
   129 Try(9 / 0) 
       
   130 
       
   131 Try(9 / 3).getOrElse(42) 
       
   132 Try(9 / 0).getOrElse(42) 
       
   133 
       
   134 
       
   135 import io.Source
    56 import io.Source
   136 
    57 
   137 val my_url = """https://nms.kcl.ac.uk/christian.urban"""
    58 Source.fromURL("""http://www.inf.kcl.ac.uk/staff/urbanc/""").mkString
   138 //val my_url = """https://nms.kcl.ac.uk/christan.urban"""  // misspelled
    59 
   139 
    60 Try(Source.fromURL("""http://www.inf.kcl.ac.uk/staff/urbanc/""").mkString).getOrElse("")
   140 Source.fromURL(my_url).mkString
    61 
   141 
    62 Try(Some(Source.fromURL("""http://www.inf.kcl.ac.uk/staff/urbanc/""").mkString)).getOrElse(None)
   142 Try(Source.fromURL(my_url).mkString).getOrElse("")
       
   143 
       
   144 Try(Some(Source.fromURL(my_url).mkString)).getOrElse(None)
       
   145 
       
   146 
    63 
   147 // a function that turns strings into numbers
    64 // a function that turns strings into numbers
   148 Integer.parseInt("1234")
    65 Integer.parseInt("12u34")
   149 
       
   150 
    66 
   151 def get_me_an_int(s: String): Option[Int] = 
    67 def get_me_an_int(s: String): Option[Int] = 
   152  Try(Some(Integer.parseInt(s))).getOrElse(None)
    68  Try(Some(Integer.parseInt(s))).getOrElse(None)
   153 
    69 
   154 val lst = List("12345", "foo", "5432", "bar", "x21")
    70 val lst = List("12345", "foo", "5432", "bar", "x21")
   155 
       
   156 for (x <- lst) yield get_me_an_int(x)
    71 for (x <- lst) yield get_me_an_int(x)
   157 
    72 
   158 // summing all the numbers
    73 // summing all the numbers
   159 val sum = (for (i <- lst) yield get_me_an_int(i)).flatten.sum
    74 val sum = lst.flatMap(get_me_an_int(_)).sum
   160 
    75 
   161 
    76 
   162 // This may not look any better than working with null in Java, but to
    77 // This may not look any better than working with null in Java, but to
   163 // see the value, you have to put yourself in the shoes of the
    78 // see the value, you have to put yourself in the shoes of the
   164 // consumer of the get_me_an_int function, and imagine you didn't
    79 // consumer of the get_me_an_int function, and imagine you didn't
   165 // write that function.
    80 // write that function.
   166 //
    81 //
   167 // In Java, if you didn't write this function, you'd have to depend on
    82 // In Java, if you didn't write this function, you'd have to depend on
   168 // the Javadoc of get_me_an_int. If you didn't look at the Javadoc, 
    83 // the Javadoc of the get_me_an_int. If you didn't look at the Javadoc, 
   169 // you might not know that get_me_an_int could return a null, and your 
    84 // you might not know that get_me_an_int could return a null, and your 
   170 // code could potentially throw a NullPointerException.
    85 // code could potentially throw a NullPointerException.
   171 
    86 
   172 
    87 
       
    88 
   173 // even Scala is not immune to problems like this:
    89 // even Scala is not immune to problems like this:
   174 
    90 
   175 List(5,6,7,8,9).indexOf(42)
    91 List(5,6,7,8,9).indexOf(7)
   176 
    92 
   177 
    93 
   178 // ... how are we supposed to know that this returns -1
    94 
   179 
    95 
   180 
    96 
   181 //other example for options...NaN
    97 // Type abbreviations
   182 val squareRoot: PartialFunction[Double, Double] = { 
    98 //====================
   183     case d: Double if d > 0 => Math.sqrt(d) 
    99 
   184 }
   100 // some syntactic convenience
   185 
   101 type Pos = (int, Int)
   186 val list: List[Double] = List(4, 16, 25, -9)
   102 
   187 
   103 type Board = List[List[Int]]
   188 val result = list.map(Math.sqrt)
   104 
   189 // => result: List[Double] = List(2.0, 4.0, 5.0, NaN)
   105 
   190 
   106 
   191 val result = list.collect(squareRoot)
   107 // Implicits
   192 // => result: List[Double] = List(2.0, 4.0, 5.0)
   108 //===========
       
   109 //
       
   110 // for example adding your own methods to Strings:
       
   111 // imagine you want to increment strings, like
       
   112 //
       
   113 //     "HAL".increment
       
   114 //
       
   115 // you can avoid ugly fudges, like a MyString, by
       
   116 // using implicit conversions
       
   117 
       
   118 
       
   119 implicit class MyString(s: String) {
       
   120   def increment = for (c <- s) yield (c + 1).toChar 
       
   121 }
       
   122 
       
   123 "HAL".increment
       
   124 
       
   125 
       
   126 // No return in Scala
       
   127 //====================
       
   128 
       
   129 //You should not use "return" in Scala:
       
   130 //
       
   131 // A return expression, when evaluated, abandons the 
       
   132 // current computation and returns to the caller of the 
       
   133 // function in which return appears."
       
   134 
       
   135 def sq1(x: Int): Int = x * x
       
   136 def sq2(x: Int): Int = return x * x
       
   137 
       
   138 def sumq(ls: List[Int]): Int = {
       
   139   (for (x <- ls) yield (return x * x)).sum[Int]
       
   140 }
       
   141 
       
   142 sumq(List(1,2,3,4))
       
   143 
       
   144 
       
   145 // last expression in a function is the return statement
       
   146 def square(x: Int): Int = {
       
   147   println(s"The argument is ${x}.")
       
   148   x * x
       
   149 }
       
   150 
       
   151 
       
   152 
       
   153 // Pattern Matching
       
   154 //==================
       
   155 
       
   156 // A powerful tool which is supposed to come to Java in a few years
       
   157 // time (https://www.youtube.com/watch?v=oGll155-vuQ)...Scala already
       
   158 // has it for many years ;o)
       
   159 
       
   160 // The general schema:
       
   161 //
       
   162 //    expression match {
       
   163 //       case pattern1 => expression1
       
   164 //       case pattern2 => expression2
       
   165 //       ...
       
   166 //       case patternN => expressionN
       
   167 //    }
       
   168 
       
   169 
       
   170 
       
   171 
       
   172 // remember
       
   173 val lst = List(None, Some(1), Some(2), None, Some(3)).flatten
       
   174 
       
   175 
       
   176 def my_flatten(xs: List[Option[Int]]): List[Int] = {
       
   177   ...
       
   178 }
       
   179 
       
   180 
       
   181 def my_flatten(lst: List[Option[Int]]): List[Int] = lst match {
       
   182   case Nil => Nil
       
   183   case None::xs => my_flatten(xs)
       
   184   case Some(n)::xs => n::my_flatten(xs)
       
   185 }
       
   186 
       
   187 
       
   188 // another example
       
   189 def get_me_a_string(n: Int): String = n match {
       
   190   case 0 => "zero"
       
   191   case 1 => "one"
       
   192   case 2 => "two"
       
   193   case _ => "many"
       
   194 }
       
   195 
       
   196 get_me_a_string(0)
       
   197 
       
   198 // you can also have cases combined
       
   199 def season(month: String) = month match {
       
   200   case "March" | "April" | "May" => "It's spring"
       
   201   case "June" | "July" | "August" => "It's summer"
       
   202   case "September" | "October" | "November" => "It's autumn"
       
   203   case "December" | "January" | "February" => "It's winter"
       
   204 }
       
   205  
       
   206 println(season("November"))
       
   207 
       
   208 // What happens if no case matches?
       
   209 
       
   210 println(season("foobar"))
       
   211 
       
   212 // fizz buzz
       
   213 def fizz_buzz(n: Int) : String = (n % 3, n % 5) match {
       
   214   case (0, 0) => "fizz buzz"
       
   215   case (0, _) => "fizz"
       
   216   case (_, 0) => "buzz"
       
   217   case _ => n.toString  
       
   218 }
       
   219 
       
   220 for (n <- 0 to 20) 
       
   221  println(fizz_buzz(n))
       
   222 
       
   223 
       
   224 // User-defined Datatypes
       
   225 //========================
       
   226 
       
   227 abstract class Tree
       
   228 case class Node(elem: Int, left: Tree, right: Tree) extends Tree
       
   229 case class Leaf() extends Tree
       
   230 
       
   231 
       
   232 def insert(tr: Tree, n: Int): Tree = tr match {
       
   233   case Leaf() => Node(n, Leaf(), Leaf())
       
   234   case Node(m, left, right) => 
       
   235     if (n == m) Node(m, left, right) 
       
   236     else if (n < m) Node(m, insert(left, n), right)
       
   237     else Node(m, left, insert(right, n))
       
   238 }
       
   239 
       
   240 
       
   241 val t1 = Node(4, Node(2, Leaf(), Leaf()), Node(7, Leaf(), Leaf()))
       
   242 insert(t1, 3)
       
   243 
       
   244 def depth(tr: Tree): Int = tr match {
       
   245   case Leaf() => 0
       
   246   case Node(_, left, right) => 1 + List(depth(left), depth(right)).max
       
   247 }
       
   248 
       
   249 
       
   250 def balance(tr: Tree): Int = tr match {
       
   251   case Leaf() => 0
       
   252   case Node(_, left, right) => depth(left) - depth(right)
       
   253 }
       
   254 
       
   255 balance(insert(t1, 3))
       
   256 
       
   257 // another example
       
   258 
       
   259 abstract class Person
       
   260 case class King() extends Person
       
   261 case class Peer(deg: String, terr: String, succ: Int) extends Person
       
   262 case class Knight(name: String) extends Person
       
   263 case class Peasant(name: String) extends Person
       
   264 case class Clown() extends Person
       
   265 
       
   266 def title(p: Person): String = p match {
       
   267   case King() => "His Majesty the King"
       
   268   case Peer(deg, terr, _) => s"The ${deg} of ${terr}"
       
   269   case Knight(name) => s"Sir ${name}"
       
   270   case Peasant(name) => name
       
   271 }
       
   272 
       
   273 def superior(p1: Person, p2: Person): Boolean = (p1, p2) match {
       
   274   case (King(), _) => true
       
   275   case (Peer(_,_,_), Knight(_)) => true
       
   276   case (Peer(_,_,_), Peasant(_)) => true
       
   277   case (Peer(_,_,_), Clown()) => true
       
   278   case (Knight(_), Peasant(_)) => true
       
   279   case (Knight(_), Clown()) => true
       
   280   case (Clown(), Peasant(_)) => true
       
   281   case _ => false
       
   282 }
       
   283 
       
   284 val people = List(Knight("David"), 
       
   285                   Peer("Duke", "Norfolk", 84), 
       
   286                   Peasant("Christian"), 
       
   287                   King(), 
       
   288                   Clown())
       
   289 
       
   290 println(people.sortWith(superior(_, _)).mkString(", "))
       
   291 
   193 
   292 
   194 
   293 
   195 // Higher-Order Functions
   294 // Higher-Order Functions
   196 //========================
   295 //========================
   197 
   296 
   198 // functions can take functions as arguments
   297 // functions can take functions as arguments
   199 
   298 
   200 val lst = (1 to 10).toList
   299 val lst = (1 to 10).toList
   201 
   300 
   202 def even(x: Int) : Boolean = x % 2 == 0
   301 def even(x: Int): Boolean = x % 2 == 0
   203 def odd(x: Int) : Boolean = x % 2 == 1
   302 def odd(x: Int): Boolean = x % 2 == 1
   204 
   303 
   205 lst.filter(x => even(x) && odd(x))
   304 lst.filter(x => even(x))
   206 lst.filter(even(_))
   305 lst.filter(even(_))
   207 lst.filter(odd && even)
   306 lst.filter(even)
   208 
   307 
   209 lst.find(_ > 8)
   308 lst.find(_ > 8)
   210 
   309 
   211 // map applies a function to each element of a list
       
   212 
       
   213 def square(x: Int): Int = x * x
   310 def square(x: Int): Int = x * x
   214 
   311 
   215 val lst = (1 to 10).toList
       
   216 lst.map(square)
   312 lst.map(square)
   217 
   313 
   218 lst.map(square).filter(_ > 4)
   314 lst.map(square).filter(_ > 4)
   219 
   315 
   220 lst.map(square).filter(_ > 4).map(square)
   316 lst.map(square).filter(_ > 4).map(square)
   221 
   317 
   222 // map works for most collection types, including sets
   318 // in my collatz.scala
   223 Set(1, 3, 6).map(square).filter(_ > 4)
   319 //(1 to bnd).map(i => (collatz(i), i)).maxBy(_._1)
   224 
   320 
   225 
   321 
   226 val l = List((1, 3),(2, 4),(4, 1),(6, 2))
   322 // type of functions, for example f: Int => Int
   227 
   323 
   228 l.map(square(_._1))
   324 def my_map_int(lst: List[Int], f: Int => Int): List[Int] = lst match {
   229 
   325   case Nil => Nil
   230 
   326   case x::xs => f(x)::my_map_int(xs, f)
   231 // Why are functions as arguments useful?
   327 }
   232 //
   328 
   233 // Consider the sum between a and b:
   329 my_map_int(lst, square)
   234 
       
   235 def sumInts(a: Int, b: Int) : Int = 
       
   236   if (a > b) 0 else a + sumInts(a + 1, b)
       
   237 
       
   238 
       
   239 sumInts(10, 16)
       
   240 
       
   241 // sum squares
       
   242 def square(n: Int) : Int = n * n
       
   243 
       
   244 def sumSquares(a: Int, b: Int) : Int = 
       
   245   if (a > b) 0 else square(a) + sumSquares(a + 1, b)
       
   246 
       
   247 sumSquares(2, 6)
       
   248 
       
   249 
       
   250 // sum factorials
       
   251 def fact(n: Int) : Int =  
       
   252   if (n == 0) 1 else n * fact(n - 1)
       
   253 
       
   254 def sumFacts(a: Int, b: Int) : Int = 
       
   255   if (a > b) 0 else fact(a) + sumFacts(a + 1, b)
       
   256 
       
   257 sumFacts(2, 6)
       
   258 
       
   259 
       
   260 
       
   261 // You can see the pattern....can we simplify our work?
       
   262 // The type of functions from ints to ints: Int => Int
       
   263 
       
   264 def sum(f: Int => Int, a: Int, b: Int) : Int = {
       
   265   if (a > b) 0 
       
   266   else f(a) + sum(f, a + 1, b)
       
   267 }
       
   268 
       
   269 
       
   270 def sumSquares(a: Int, b: Int) : Int = sum(square, a, b)
       
   271 def sumFacts(a: Int, b: Int) : Int = sum(fact, a, b)
       
   272 
       
   273 // What should we do for sumInts?
       
   274 
       
   275 def id(n: Int) : Int = n
       
   276 def sumInts(a: Int, b: Int) : Int = sum(id, a, b)
       
   277 
       
   278 sumInts(10, 12)
       
   279 
       
   280 
       
   281 // Anonymous Functions: You can also write:
       
   282 
       
   283 def sumCubes(a: Int, b: Int) : Int =   sum(x => x * x * x, a, b)
       
   284 def sumSquares(a: Int, b: Int) : Int = sum(x => x * x, a, b)
       
   285 def sumInts(a: Int, b: Int) : Int    = sum(x => x, a, b)
       
   286 
       
   287 
   330 
   288 // other function types
   331 // other function types
   289 //
   332 //
   290 // f1: (Int, Int) => Int
   333 // f1: (Int, Int) => Int
   291 // f2: List[String] => Option[Int]
   334 // f2: List[String] => Option[Int]
   292 // ... 
   335 // ... 
   293 
   336 
   294 
   337 
   295 // an aside: partial application
   338 def sumOf(f: Int => Int, lst: List[Int]): Int = lst match {
   296 
   339   case Nil => 0
   297 def add(a: Int)(b: Int) : Int = a + b
   340   case x::xs => f(x) + sumOf(f, xs)
   298 def add_abc(a: Int)(b: Int)(c: Int) : Int = a + b + c
   341 }
   299 
   342 
   300 val add2 : Int => Int = add(2)
   343 def sum_squares(lst: List[Int]) = sumOf(square, lst)
   301 add2(5)
   344 def sum_cubes(lst: List[Int])   = sumOf(x => x * x * x, lst)
   302 
   345 
   303 val add2_bc : Int => Int => Int = add_abc(2) 
   346 sum_squares(lst)
   304 val add2_9_c : Int => Int = add2_bc(9) 
   347 sum_cubes(lst)
   305 
   348 
   306 add2_9_c(10)
   349 // lets try it factorial
   307 
   350 def fact(n: Int): Int = ...
   308 sum(add(2), 0, 2)
   351 
   309 sum(add(10), 0, 2)
   352 def sum_fact(lst: List[Int]) = sumOf(fact, lst)
   310 
   353 sum_fact(lst)
   311 
   354 
   312 
   355 // Avoid being mutable
   313 
   356 //=====================
   314 // some automatic timing in each evaluation
   357 
   315 package wrappers {  
   358 // a student showed me...
   316 
   359 import scala.collection.mutable.ListBuffer
   317   object wrap { 
   360 
   318    
   361 
   319     def timed[R](block: => R): R = {
   362 
   320       val t0 = System.nanoTime()
   363 def collatz_max(bnd: Long): (Long, Long) = {
   321       val result = block
   364   val colNos = ListBuffer[(Long, Long)]()
   322       println("Elapsed time: " + (System.nanoTime - t0) + "ns")
   365   for (i <- (1L to bnd).toList) colNos += ((collatz(i), i))
   323       result
   366   colNos.max
   324     }
   367 }
   325 
   368 
   326     def apply[A](a: => A): A = { 
   369 def collatz_max(bnd: Long): (Long, Long) = {
   327       timed(a)
   370   (1L to bnd).map((i) => (collatz(i), i)).maxBy(_._1)
   328     } 
   371 }
   329   }
   372 
   330 }
   373 //views -> lazy collection
   331 
   374 def collatz_max(bnd: Long): (Long, Long) = {
   332 $intp.setExecutionWrapper("wrappers.wrap")
   375   (1L to bnd).view.map((i) => (collatz(i), i)).maxBy(_._1)
   333 
   376 }
   334 // Iteration
   377 
   335 
   378 // raises a GC exception
   336 def fib(n: Int) : Int = 
   379 (1 to 1000000000).filter(_ % 2 == 0).take(10).toList
   337   if (n <= 1) 1 else fib(n - 1) + fib(n - 2)
   380 // ==> java.lang.OutOfMemoryError: GC overhead limit exceeded
   338 
   381 
   339 fib(10)
   382 (1 to 1000000000).view.filter(_ % 2 == 0).take(10).toList
   340 
   383 
   341 
   384 
   342 Iterator.iterate((1,1)){ case (n: Int, m: Int) => (n + m, n) }.drop(9).next
   385 
   343 
   386 // Sudoku
   344 
   387 //========
   345 
       
   346 
       
   347 // Function Composition
       
   348 //======================
       
   349 
       
   350 // How can be Higher-Order Functions and Options be helpful?
       
   351 
       
   352 def add_footer(msg: String) : String = msg ++ " - Sent from iOS"
       
   353 
       
   354 def valid_msg(msg: String) : Boolean = msg.size <= 140
       
   355 
       
   356 def duplicate(s: String) : String = s ++ s
       
   357 
       
   358 // they compose very nicely, e.g
       
   359 
       
   360 valid_msg(add_footer("Hello World"))
       
   361 valid_msg(duplicate(duplicate(add_footer("Helloooooooooooooooooo World"))))
       
   362 
       
   363 // but not all functions do
       
   364 // first_word: let's first do it the ugly Java way using null:
       
   365 
       
   366 def first_word(msg: String) : String = {
       
   367   val words = msg.split(" ")
       
   368   if (words(0) != "") words(0) else null
       
   369 }
       
   370 
       
   371 duplicate(first_word("Hello World"))
       
   372 duplicate(first_word(""))
       
   373 
       
   374 def extended_duplicate(s: String) : String = 
       
   375   if (s != null) s ++ s else null
       
   376 
       
   377 extended_duplicate(first_word(""))
       
   378 
       
   379 // but this is against the rules of the game: we do not want
       
   380 // to change duplicate, because first_word might return null
       
   381 
       
   382 
       
   383 // Avoid always null!
       
   384 def better_first_word(msg: String) : Option[String] = {
       
   385   val words = msg.split(" ")
       
   386   if (words(0) != "") Some(words(0)) else None
       
   387 }
       
   388 
       
   389 better_first_word("Hello World").map(duplicate)
       
   390 
       
   391 better_first_word("Hello World").map(duplicate)
       
   392 better_first_word("").map(duplicate).map(duplicate).map(valid_msg)
       
   393 
       
   394 better_first_word("").map(duplicate)
       
   395 better_first_word("").map(duplicate).map(valid_msg)
       
   396 
       
   397 
       
   398 
       
   399 
       
   400 
       
   401 // Problems with mutability and parallel computations
       
   402 //====================================================
       
   403 
       
   404 def count_intersection(A: Set[Int], B: Set[Int]) : Int = {
       
   405   var count = 0
       
   406   for (x <- A; if (B contains x)) count += 1 
       
   407   count
       
   408 }
       
   409 
       
   410 val A = (1 to 1000).toSet
       
   411 val B = (1 to 1000 by 4).toSet
       
   412 
       
   413 count_intersection(A, B)
       
   414 
       
   415 // but do not try to add .par to the for-loop above,
       
   416 // otherwise you will be caught in race-condition hell.
       
   417 
       
   418 
       
   419 //propper parallel version
       
   420 def count_intersection2(A: Set[Int], B: Set[Int]) : Int = 
       
   421   A.par.count(x => B contains x)
       
   422 
       
   423 count_intersection2(A, B)
       
   424 
       
   425 
       
   426 //for measuring time
       
   427 def time_needed[T](n: Int, code: => T) = {
       
   428   val start = System.nanoTime()
       
   429   for (i <- (0 to n)) code
       
   430   val end = System.nanoTime()
       
   431   (end - start) / 1.0e9
       
   432 }
       
   433 
       
   434 val A = (1 to 1000000).toSet
       
   435 val B = (1 to 1000000 by 4).toSet
       
   436 
       
   437 time_needed(10, count_intersection(A, B))
       
   438 time_needed(10, count_intersection2(A, B))
       
   439 
       
   440 
       
   441 
       
   442 
       
   443 
       
   444 
       
   445 // No returns in Scala
       
   446 //====================
       
   447 
       
   448 // You should not use "return" in Scala:
       
   449 //
       
   450 // A return expression, when evaluated, abandons the 
       
   451 // current computation and returns to the caller of the 
       
   452 // function in which return appears."
       
   453 
       
   454 def sq1(x: Int): Int = x * x
       
   455 def sumq(ls: List[Int]): Int = 
       
   456   ls.map(x => x * x).sum
       
   457 
       
   458 
       
   459 
       
   460 
       
   461 def sq2(x: Int): Int = return x * x
       
   462 
       
   463 def sumq(ls: List[Int]): Int = {
       
   464   ls.map(sq1).sum[Int]
       
   465 }
       
   466 
       
   467 sumq(List(1, 2, 3, 4))
       
   468 
       
   469 
       
   470 
       
   471 def sumq(ls: List[Int]): Int = {
       
   472   val sqs : List[Int] = for (x <- ls) yield (return x * x)
       
   473   sqs.sum
       
   474 }
       
   475 
       
   476 sumq(List(1, 2, 3, 4))
       
   477 
       
   478 
       
   479 
       
   480 // Type abbreviations
       
   481 //====================
       
   482 
       
   483 // some syntactic convenience
       
   484 
       
   485 type Pos = (int, Int)
       
   486 type Board = List[List[Int]]
       
   487 
       
   488 
       
   489 
       
   490 
       
   491 // Sudoku in Scala
       
   492 //=================
       
   493 
   388 
   494 // THE POINT OF THIS CODE IS NOT TO BE SUPER
   389 // THE POINT OF THIS CODE IS NOT TO BE SUPER
   495 // EFFICIENT AND FAST, just explaining exhaustive
   390 // EFFICIENT AND FAST, just explaining exhaustive
   496 // depth-first search
   391 // depth-first search
   497 
   392 
   512 
   407 
   513 val allValues = "123456789".toList
   408 val allValues = "123456789".toList
   514 val indexes = (0 to 8).toList
   409 val indexes = (0 to 8).toList
   515 
   410 
   516 
   411 
       
   412 
       
   413 
   517 def empty(game: String) = game.indexOf(EmptyValue)
   414 def empty(game: String) = game.indexOf(EmptyValue)
   518 def isDone(game: String) = empty(game) == -1 
   415 def isDone(game: String) = empty(game) == -1 
   519 def emptyPosition(game: String) = 
   416 def emptyPosition(game: String) = (empty(game) % MaxValue, empty(game) / MaxValue)
   520   (empty(game) % MaxValue, empty(game) / MaxValue)
   417 
   521 
   418 
   522 
   419 def get_row(game: String, y: Int) = indexes.map(col => game(y * MaxValue + col))
   523 def get_row(game: String, y: Int) = 
   420 def get_col(game: String, x: Int) = indexes.map(row => game(x + row * MaxValue))
   524   indexes.map(col => game(y * MaxValue + col))
       
   525 def get_col(game: String, x: Int) = 
       
   526   indexes.map(row => game(x + row * MaxValue))
       
   527 
       
   528 get_row(game0, 3)
       
   529 get_col(game0, 0)
       
   530 
   421 
   531 def get_box(game: String, pos: Pos): List[Char] = {
   422 def get_box(game: String, pos: Pos): List[Char] = {
   532     def base(p: Int): Int = (p / 3) * 3
   423     def base(p: Int): Int = (p / 3) * 3
   533     val x0 = base(pos._1)
   424     val x0 = base(pos._1)
   534     val y0 = base(pos._2)
   425     val y0 = base(pos._2)
   535     val ys = (y0 until y0 + 3).toList
   426     val ys = (y0 until y0 + 3).toList
   536     (x0 until x0 + 3).toList.flatMap(x => ys.map(y => game(x + y * MaxValue)))
   427     (x0 until x0 + 3).toList.flatMap(x => ys.map(y => game(x + y * MaxValue)))
   537 }
   428 }
   538 
   429 
   539 get_box(game0, (0, 0))
   430 
   540 get_box(game0, (1, 1))
   431 //get_row(game0, 0)
   541 get_box(game0, (2, 1))
   432 //get_row(game0, 1)
   542 
   433 //get_box(game0, (3,1))
   543 // this is not mutable!!
   434 
   544 def update(game: String, pos: Int, value: Char): String = 
   435 def update(game: String, pos: Int, value: Char): String = game.updated(pos, value)
   545   game.updated(pos, value)
       
   546 
   436 
   547 def toAvoid(game: String, pos: Pos): List[Char] = 
   437 def toAvoid(game: String, pos: Pos): List[Char] = 
   548   (get_col(game, pos._1) ++ get_row(game, pos._2) ++ get_box(game, pos))
   438   (get_col(game, pos._1) ++ get_row(game, pos._2) ++ get_box(game, pos))
   549 
   439 
   550 def candidates(game: String, pos: Pos): List[Char] = 
   440 def candidates(game: String, pos: Pos): List[Char] = allValues diff toAvoid(game,pos)
   551   allValues.diff(toAvoid(game,pos))
       
   552 
   441 
   553 //candidates(game0, (0,0))
   442 //candidates(game0, (0,0))
   554 
   443 
   555 def pretty(game: String): String = 
   444 def pretty(game: String): String = "\n" + (game sliding (MaxValue, MaxValue) mkString "\n")
   556   "\n" + (game sliding (MaxValue, MaxValue) mkString "\n")
       
   557 
   445 
   558 def search(game: String): List[String] = {
   446 def search(game: String): List[String] = {
   559   if (isDone(game)) List(game)
   447   if (isDone(game)) List(game)
   560   else {
   448   else 
   561     val cs = candidates(game, emptyPosition(game))
   449     candidates(game, emptyPosition(game)).map(c => search(update(game, empty(game), c))).toList.flatten
   562     cs.par.map(c => search(update(game, empty(game), c))).toList.flatten
   450 }
   563   }
   451 
   564 }
       
   565 
       
   566 search(game0).map(pretty)
       
   567 
   452 
   568 val game1 = """23.915...
   453 val game1 = """23.915...
   569               |...2..54.
   454               |...2..54.
   570               |6.7......
   455               |6.7......
   571               |..1.....9
   456               |..1.....9
   573               |5.....6..
   458               |5.....6..
   574               |......9.5
   459               |......9.5
   575               |.16..7...
   460               |.16..7...
   576               |...329..1""".stripMargin.replaceAll("\\n", "")
   461               |...329..1""".stripMargin.replaceAll("\\n", "")
   577 
   462 
   578 search(game1).map(pretty)
   463 
   579 
   464 // game that is in the hard category
   580 // game that is in the hard(er) category
       
   581 val game2 = """8........
   465 val game2 = """8........
   582               |..36.....
   466               |..36.....
   583               |.7..9.2..
   467               |.7..9.2..
   584               |.5...7...
   468               |.5...7...
   585               |....457..
   469               |....457..
   598               |.......7.
   482               |.......7.
   599               |.3.5...8.
   483               |.3.5...8.
   600               |9724...5.""".stripMargin.replaceAll("\\n", "")
   484               |9724...5.""".stripMargin.replaceAll("\\n", "")
   601 
   485 
   602 
   486 
   603 search(game2).map(pretty)
   487 search(game0).map(pretty)
   604 search(game3).map(pretty)
   488 search(game1).map(pretty)
   605 
   489 
   606 // for measuring time
   490 // for measuring time
   607 def time_needed[T](i: Int, code: => T) = {
   491 def time_needed[T](i: Int, code: => T) = {
   608   val start = System.nanoTime()
   492   val start = System.nanoTime()
   609   for (j <- 1 to i) code
   493   for (j <- 1 to i) code
   611   ((end - start) / i / 1.0e9) + " secs"
   495   ((end - start) / i / 1.0e9) + " secs"
   612 }
   496 }
   613 
   497 
   614 search(game2).map(pretty)
   498 search(game2).map(pretty)
   615 search(game3).distinct.length
   499 search(game3).distinct.length
   616 time_needed(1, search(game2))
   500 time_needed(3, search(game2))
   617 time_needed(1, search(game3))
   501 time_needed(3, search(game3))
   618 
   502 
   619 
   503 
   620 
   504 
   621 
   505 
   622 //===================
   506 
   623 // the end for today