| 51 |      1 | // Scala Lecture 2
 | 
|  |      2 | //=================
 | 
|  |      3 | 
 | 
|  |      4 | 
 | 
| 148 |      5 | // the pain with overloaded math operations
 | 
| 147 |      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 | 
 | 
| 150 |     42 | for (e <- lst; if (e % 2) == 0; if (e != 4)) yield square(e)
 | 
| 147 |     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),
 | 
| 150 |     58 | // for has no "yield"
 | 
| 147 |     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 | 
 | 
| 150 |     74 | val test = 
 | 
|  |     75 |  for (e <- Set(1, 2, 3, 4, 5, 6, 7, 8, 9); if e < 5) yield square(e)
 | 
|  |     76 | 
 | 
| 147 |     77 | 
 | 
|  |     78 | 
 | 
| 51 |     79 | // Option type
 | 
|  |     80 | //=============
 | 
| 53 |     81 | 
 | 
| 147 |     82 | //in Java, if something unusually happens, you return null;
 | 
| 53 |     83 | //in Scala you use Option
 | 
|  |     84 | //   - if the value is present, you use Some(value)
 | 
|  |     85 | //   - if no value is present, you use None
 | 
|  |     86 | 
 | 
|  |     87 | 
 | 
| 150 |     88 | List(7,24,3,4,5,6).find(_ < 4)
 | 
| 53 |     89 | List(5,6,7,8,9).find(_ < 4)
 | 
|  |     90 | 
 | 
| 150 |     91 | List(7,2,3,4,5,6).filter(_ < 4)
 | 
| 58 |     92 | 
 | 
| 147 |     93 | // some operations on Option's
 | 
| 58 |     94 | 
 | 
| 51 |     95 | val lst = List(None, Some(1), Some(2), None, Some(3))
 | 
|  |     96 | 
 | 
|  |     97 | lst.flatten
 | 
| 53 |     98 | 
 | 
| 150 |     99 | Some(10).get
 | 
|  |    100 | None.get
 | 
| 51 |    101 | 
 | 
| 53 |    102 | Some(1).isDefined
 | 
|  |    103 | None.isDefined
 | 
|  |    104 | 
 | 
| 51 |    105 | val ps = List((3, 0), (3, 2), (4, 2), (2, 0), (1, 0), (1, 1))
 | 
|  |    106 | 
 | 
|  |    107 | for ((x, y) <- ps) yield {
 | 
|  |    108 |   if (y == 0) None else Some(x / y)
 | 
|  |    109 | }
 | 
|  |    110 | 
 | 
| 147 |    111 | // use .getOrElse is for setting a default value
 | 
| 53 |    112 | 
 | 
|  |    113 | val lst = List(None, Some(1), Some(2), None, Some(3))
 | 
| 147 |    114 | 
 | 
| 57 |    115 | for (x <- lst) yield x.getOrElse(0)
 | 
|  |    116 | 
 | 
|  |    117 | 
 | 
| 53 |    118 | 
 | 
|  |    119 | 
 | 
| 147 |    120 | // error handling with Options (no exceptions)
 | 
|  |    121 | //
 | 
|  |    122 | //  Try(....)
 | 
| 57 |    123 | //
 | 
|  |    124 | //  Try(something).getOrElse(what_to_do_in_an_exception)
 | 
|  |    125 | //
 | 
| 53 |    126 | import scala.util._
 | 
| 147 |    127 | 
 | 
|  |    128 | Try(1 + 3)
 | 
|  |    129 | Try(9 / 0) 
 | 
|  |    130 | 
 | 
|  |    131 | Try(9 / 3).getOrElse(42) 
 | 
|  |    132 | Try(9 / 0).getOrElse(42) 
 | 
|  |    133 | 
 | 
|  |    134 | 
 | 
| 53 |    135 | import io.Source
 | 
|  |    136 | 
 | 
| 147 |    137 | val my_url = """https://nms.kcl.ac.uk/christian.urban"""
 | 
| 150 |    138 | //val my_url = """https://nms.kcl.ac.uk/christan.urban"""  // misspelled
 | 
| 147 |    139 | 
 | 
|  |    140 | Source.fromURL(my_url).mkString
 | 
| 53 |    141 | 
 | 
| 147 |    142 | Try(Source.fromURL(my_url).mkString).getOrElse("")
 | 
| 53 |    143 | 
 | 
| 147 |    144 | Try(Some(Source.fromURL(my_url).mkString)).getOrElse(None)
 | 
|  |    145 | 
 | 
| 53 |    146 | 
 | 
| 57 |    147 | // a function that turns strings into numbers
 | 
| 147 |    148 | Integer.parseInt("1234")
 | 
|  |    149 | 
 | 
| 53 |    150 | 
 | 
|  |    151 | def get_me_an_int(s: String): Option[Int] = 
 | 
|  |    152 |  Try(Some(Integer.parseInt(s))).getOrElse(None)
 | 
|  |    153 | 
 | 
|  |    154 | val lst = List("12345", "foo", "5432", "bar", "x21")
 | 
| 147 |    155 | 
 | 
| 53 |    156 | for (x <- lst) yield get_me_an_int(x)
 | 
|  |    157 | 
 | 
|  |    158 | // summing all the numbers
 | 
| 147 |    159 | val sum = (for (i <- lst) yield get_me_an_int(i)).flatten.sum
 | 
| 53 |    160 | 
 | 
|  |    161 | 
 | 
|  |    162 | // 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
 | 
|  |    164 | // consumer of the get_me_an_int function, and imagine you didn't
 | 
|  |    165 | // write that function.
 | 
|  |    166 | //
 | 
|  |    167 | // In Java, if you didn't write this function, you'd have to depend on
 | 
| 147 |    168 | // the Javadoc of get_me_an_int. If you didn't look at the Javadoc, 
 | 
| 57 |    169 | // you might not know that get_me_an_int could return a null, and your 
 | 
|  |    170 | // code could potentially throw a NullPointerException.
 | 
| 53 |    171 | 
 | 
|  |    172 | 
 | 
| 58 |    173 | // even Scala is not immune to problems like this:
 | 
|  |    174 | 
 | 
| 150 |    175 | List(5,6,7,8,9).indexOf(42)
 | 
| 58 |    176 | 
 | 
|  |    177 | 
 | 
| 150 |    178 | // ... how are we supposed to know that this returns -1
 | 
| 58 |    179 | 
 | 
|  |    180 | 
 | 
| 173 |    181 | //other example for options...NaN
 | 
|  |    182 | val squareRoot: PartialFunction[Double, Double] = { 
 | 
|  |    183 |     case d: Double if d > 0 => Math.sqrt(d) 
 | 
|  |    184 | }
 | 
|  |    185 | 
 | 
|  |    186 | val list: List[Double] = List(4, 16, 25, -9)
 | 
|  |    187 | 
 | 
|  |    188 | val result = list.map(Math.sqrt)
 | 
|  |    189 | // => result: List[Double] = List(2.0, 4.0, 5.0, NaN)
 | 
|  |    190 | 
 | 
|  |    191 | val result = list.collect(squareRoot)
 | 
|  |    192 | // => result: List[Double] = List(2.0, 4.0, 5.0)
 | 
|  |    193 | 
 | 
|  |    194 | 
 | 
| 147 |    195 | // Higher-Order Functions
 | 
|  |    196 | //========================
 | 
|  |    197 | 
 | 
|  |    198 | // functions can take functions as arguments
 | 
|  |    199 | 
 | 
|  |    200 | val lst = (1 to 10).toList
 | 
|  |    201 | 
 | 
|  |    202 | def even(x: Int) : Boolean = x % 2 == 0
 | 
|  |    203 | def odd(x: Int) : Boolean = x % 2 == 1
 | 
|  |    204 | 
 | 
| 150 |    205 | lst.filter(x => even(x) && odd(x))
 | 
| 147 |    206 | lst.filter(even(_))
 | 
| 150 |    207 | lst.filter(odd && even)
 | 
| 147 |    208 | 
 | 
|  |    209 | lst.find(_ > 8)
 | 
|  |    210 | 
 | 
|  |    211 | // map applies a function to each element of a list
 | 
|  |    212 | 
 | 
|  |    213 | def square(x: Int): Int = x * x
 | 
|  |    214 | 
 | 
| 150 |    215 | val lst = (1 to 10).toList
 | 
| 147 |    216 | lst.map(square)
 | 
|  |    217 | 
 | 
|  |    218 | lst.map(square).filter(_ > 4)
 | 
|  |    219 | 
 | 
|  |    220 | lst.map(square).filter(_ > 4).map(square)
 | 
|  |    221 | 
 | 
|  |    222 | // map works for most collection types, including sets
 | 
| 150 |    223 | Set(1, 3, 6).map(square).filter(_ > 4)
 | 
| 147 |    224 | 
 | 
|  |    225 | 
 | 
| 150 |    226 | val l = List((1, 3),(2, 4),(4, 1),(6, 2))
 | 
|  |    227 | 
 | 
|  |    228 | l.map(square(_._1))
 | 
|  |    229 | 
 | 
|  |    230 | 
 | 
|  |    231 | // Why are functions as arguments useful?
 | 
| 147 |    232 | //
 | 
|  |    233 | // Consider the sum between a and b:
 | 
|  |    234 | 
 | 
|  |    235 | def sumInts(a: Int, b: Int) : Int = 
 | 
|  |    236 |   if (a > b) 0 else a + sumInts(a + 1, b)
 | 
|  |    237 | 
 | 
|  |    238 | 
 | 
| 150 |    239 | sumInts(10, 16)
 | 
| 147 |    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 | 
 | 
| 150 |    261 | // You can see the pattern....can we simplify our work?
 | 
| 147 |    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 | 
 | 
| 150 |    278 | sumInts(10, 12)
 | 
| 147 |    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 | 
 | 
|  |    288 | // other function types
 | 
|  |    289 | //
 | 
|  |    290 | // f1: (Int, Int) => Int
 | 
|  |    291 | // f2: List[String] => Option[Int]
 | 
|  |    292 | // ... 
 | 
|  |    293 | 
 | 
|  |    294 | 
 | 
| 148 |    295 | // an aside: partial application
 | 
|  |    296 | 
 | 
|  |    297 | def add(a: Int)(b: Int) : Int = a + b
 | 
| 150 |    298 | def add_abc(a: Int)(b: Int)(c: Int) : Int = a + b + c
 | 
|  |    299 | 
 | 
|  |    300 | val add2 : Int => Int = add(2)
 | 
|  |    301 | add2(5)
 | 
|  |    302 | 
 | 
|  |    303 | val add2_bc : Int => Int => Int = add_abc(2) 
 | 
|  |    304 | val add2_9_c : Int => Int = add2_bc(9) 
 | 
|  |    305 | 
 | 
|  |    306 | add2_9_c(10)
 | 
| 148 |    307 | 
 | 
|  |    308 | sum(add(2), 0, 2)
 | 
|  |    309 | sum(add(10), 0, 2)
 | 
|  |    310 | 
 | 
|  |    311 | 
 | 
| 167 |    312 | 
 | 
|  |    313 | 
 | 
|  |    314 | // some automatic timing in each evaluation
 | 
|  |    315 | package wrappers {  
 | 
|  |    316 | 
 | 
|  |    317 |   object wrap { 
 | 
|  |    318 |    
 | 
|  |    319 |     def timed[R](block: => R): R = {
 | 
|  |    320 |       val t0 = System.nanoTime()
 | 
|  |    321 |       val result = block
 | 
|  |    322 |       println("Elapsed time: " + (System.nanoTime - t0) + "ns")
 | 
|  |    323 |       result
 | 
|  |    324 |     }
 | 
|  |    325 | 
 | 
|  |    326 |     def apply[A](a: => A): A = { 
 | 
|  |    327 |       timed(a)
 | 
|  |    328 |     } 
 | 
|  |    329 |   }
 | 
|  |    330 | }
 | 
|  |    331 | 
 | 
|  |    332 | $intp.setExecutionWrapper("wrappers.wrap")
 | 
|  |    333 | 
 | 
|  |    334 | // Iteration
 | 
|  |    335 | 
 | 
|  |    336 | def fib(n: Int) : Int = 
 | 
|  |    337 |   if (n <= 1) 1 else fib(n - 1) + fib(n - 2)
 | 
|  |    338 | 
 | 
|  |    339 | fib(10)
 | 
|  |    340 | 
 | 
|  |    341 | 
 | 
|  |    342 | Iterator.iterate((1,1)){ case (n: Int, m: Int) => (n + m, n) }.drop(9).next
 | 
|  |    343 | 
 | 
|  |    344 | 
 | 
|  |    345 | 
 | 
|  |    346 | 
 | 
| 147 |    347 | // Function Composition
 | 
|  |    348 | //======================
 | 
|  |    349 | 
 | 
| 150 |    350 | // How can be Higher-Order Functions and Options be helpful?
 | 
| 147 |    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 | 
 | 
| 150 |    358 | // they compose very nicely, e.g
 | 
|  |    359 | 
 | 
| 147 |    360 | valid_msg(add_footer("Hello World"))
 | 
| 150 |    361 | valid_msg(duplicate(duplicate(add_footer("Helloooooooooooooooooo World"))))
 | 
| 147 |    362 | 
 | 
| 150 |    363 | // but not all functions do
 | 
| 147 |    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 | 
 | 
| 150 |    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 | 
 | 
| 147 |    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)
 | 
| 150 |    390 | 
 | 
|  |    391 | better_first_word("Hello World").map(duplicate)
 | 
|  |    392 | better_first_word("").map(duplicate).map(duplicate).map(valid_msg)
 | 
| 147 |    393 | 
 | 
|  |    394 | better_first_word("").map(duplicate)
 | 
|  |    395 | better_first_word("").map(duplicate).map(valid_msg)
 | 
|  |    396 | 
 | 
|  |    397 | 
 | 
| 150 |    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))
 | 
| 53 |    439 | 
 | 
|  |    440 | 
 | 
| 57 |    441 | 
 | 
|  |    442 | 
 | 
|  |    443 | 
 | 
| 147 |    444 | 
 | 
|  |    445 | // No returns in Scala
 | 
| 53 |    446 | //====================
 | 
|  |    447 | 
 | 
| 147 |    448 | // You should not use "return" in Scala:
 | 
| 53 |    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
 | 
| 174 |    455 | def sumq(ls: List[Int]): Int = 
 | 
|  |    456 |   ls.map(x => x * x).sum
 | 
|  |    457 | 
 | 
|  |    458 | 
 | 
|  |    459 | 
 | 
|  |    460 | 
 | 
| 53 |    461 | def sq2(x: Int): Int = return x * x
 | 
|  |    462 | 
 | 
|  |    463 | def sumq(ls: List[Int]): Int = {
 | 
| 147 |    464 |   ls.map(sq1).sum[Int]
 | 
| 53 |    465 | }
 | 
|  |    466 | 
 | 
| 147 |    467 | sumq(List(1, 2, 3, 4))
 | 
| 36 |    468 | 
 | 
| 57 |    469 | 
 | 
| 147 |    470 | 
 | 
|  |    471 | def sumq(ls: List[Int]): Int = {
 | 
|  |    472 |   val sqs : List[Int] = for (x <- ls) yield (return x * x)
 | 
|  |    473 |   sqs.sum
 | 
| 53 |    474 | }
 | 
|  |    475 | 
 | 
| 174 |    476 | sumq(List(1, 2, 3, 4))
 | 
| 55 |    477 | 
 | 
|  |    478 | 
 | 
| 147 |    479 | 
 | 
| 148 |    480 | // Type abbreviations
 | 
|  |    481 | //====================
 | 
|  |    482 | 
 | 
|  |    483 | // some syntactic convenience
 | 
|  |    484 | 
 | 
|  |    485 | type Pos = (int, Int)
 | 
|  |    486 | type Board = List[List[Int]]
 | 
|  |    487 | 
 | 
| 56 |    488 | 
 | 
| 57 |    489 | 
 | 
|  |    490 | 
 | 
| 150 |    491 | // Sudoku in Scala
 | 
|  |    492 | //=================
 | 
| 53 |    493 | 
 | 
| 57 |    494 | // THE POINT OF THIS CODE IS NOT TO BE SUPER
 | 
|  |    495 | // EFFICIENT AND FAST, just explaining exhaustive
 | 
|  |    496 | // depth-first search
 | 
|  |    497 | 
 | 
|  |    498 | 
 | 
| 55 |    499 | val game0 = """.14.6.3..
 | 
|  |    500 |               |62...4..9
 | 
|  |    501 |               |.8..5.6..
 | 
|  |    502 |               |.6.2....3
 | 
|  |    503 |               |.7..1..5.
 | 
|  |    504 |               |5....9.6.
 | 
|  |    505 |               |..6.2..3.
 | 
|  |    506 |               |1..5...92
 | 
|  |    507 |               |..7.9.41.""".stripMargin.replaceAll("\\n", "")
 | 
|  |    508 | 
 | 
|  |    509 | type Pos = (Int, Int)
 | 
|  |    510 | val EmptyValue = '.'
 | 
|  |    511 | val MaxValue = 9
 | 
|  |    512 | 
 | 
|  |    513 | val allValues = "123456789".toList
 | 
|  |    514 | val indexes = (0 to 8).toList
 | 
|  |    515 | 
 | 
| 57 |    516 | 
 | 
|  |    517 | def empty(game: String) = game.indexOf(EmptyValue)
 | 
|  |    518 | def isDone(game: String) = empty(game) == -1 
 | 
| 150 |    519 | def emptyPosition(game: String) = 
 | 
|  |    520 |   (empty(game) % MaxValue, empty(game) / MaxValue)
 | 
| 57 |    521 | 
 | 
| 55 |    522 | 
 | 
| 150 |    523 | def get_row(game: String, y: Int) = 
 | 
|  |    524 |   indexes.map(col => game(y * MaxValue + col))
 | 
|  |    525 | def get_col(game: String, x: Int) = 
 | 
|  |    526 |   indexes.map(row => game(x + row * MaxValue))
 | 
| 57 |    527 | 
 | 
| 147 |    528 | get_row(game0, 3)
 | 
|  |    529 | get_col(game0, 0)
 | 
|  |    530 | 
 | 
| 57 |    531 | def get_box(game: String, pos: Pos): List[Char] = {
 | 
| 55 |    532 |     def base(p: Int): Int = (p / 3) * 3
 | 
|  |    533 |     val x0 = base(pos._1)
 | 
|  |    534 |     val y0 = base(pos._2)
 | 
|  |    535 |     val ys = (y0 until y0 + 3).toList
 | 
|  |    536 |     (x0 until x0 + 3).toList.flatMap(x => ys.map(y => game(x + y * MaxValue)))
 | 
|  |    537 | }
 | 
|  |    538 | 
 | 
| 147 |    539 | get_box(game0, (0, 0))
 | 
|  |    540 | get_box(game0, (1, 1))
 | 
|  |    541 | get_box(game0, (2, 1))
 | 
| 55 |    542 | 
 | 
| 147 |    543 | // this is not mutable!!
 | 
| 150 |    544 | def update(game: String, pos: Int, value: Char): String = 
 | 
|  |    545 |   game.updated(pos, value)
 | 
| 55 |    546 | 
 | 
|  |    547 | def toAvoid(game: String, pos: Pos): List[Char] = 
 | 
| 57 |    548 |   (get_col(game, pos._1) ++ get_row(game, pos._2) ++ get_box(game, pos))
 | 
| 55 |    549 | 
 | 
| 150 |    550 | def candidates(game: String, pos: Pos): List[Char] = 
 | 
|  |    551 |   allValues.diff(toAvoid(game,pos))
 | 
| 55 |    552 | 
 | 
|  |    553 | //candidates(game0, (0,0))
 | 
|  |    554 | 
 | 
| 147 |    555 | def pretty(game: String): String = 
 | 
|  |    556 |   "\n" + (game sliding (MaxValue, MaxValue) mkString "\n")
 | 
| 55 |    557 | 
 | 
|  |    558 | def search(game: String): List[String] = {
 | 
|  |    559 |   if (isDone(game)) List(game)
 | 
| 147 |    560 |   else {
 | 
|  |    561 |     val cs = candidates(game, emptyPosition(game))
 | 
| 150 |    562 |     cs.par.map(c => search(update(game, empty(game), c))).toList.flatten
 | 
| 147 |    563 |   }
 | 
| 55 |    564 | }
 | 
|  |    565 | 
 | 
| 147 |    566 | search(game0).map(pretty)
 | 
| 55 |    567 | 
 | 
|  |    568 | val game1 = """23.915...
 | 
|  |    569 |               |...2..54.
 | 
|  |    570 |               |6.7......
 | 
|  |    571 |               |..1.....9
 | 
|  |    572 |               |89.5.3.17
 | 
|  |    573 |               |5.....6..
 | 
|  |    574 |               |......9.5
 | 
|  |    575 |               |.16..7...
 | 
|  |    576 |               |...329..1""".stripMargin.replaceAll("\\n", "")
 | 
|  |    577 | 
 | 
| 147 |    578 | search(game1).map(pretty)
 | 
| 57 |    579 | 
 | 
| 147 |    580 | // game that is in the hard(er) category
 | 
| 55 |    581 | val game2 = """8........
 | 
|  |    582 |               |..36.....
 | 
|  |    583 |               |.7..9.2..
 | 
|  |    584 |               |.5...7...
 | 
|  |    585 |               |....457..
 | 
|  |    586 |               |...1...3.
 | 
|  |    587 |               |..1....68
 | 
|  |    588 |               |..85...1.
 | 
|  |    589 |               |.9....4..""".stripMargin.replaceAll("\\n", "")
 | 
|  |    590 | 
 | 
|  |    591 | // game with multiple solutions
 | 
|  |    592 | val game3 = """.8...9743
 | 
|  |    593 |               |.5...8.1.
 | 
|  |    594 |               |.1.......
 | 
|  |    595 |               |8....5...
 | 
|  |    596 |               |...8.4...
 | 
|  |    597 |               |...3....6
 | 
|  |    598 |               |.......7.
 | 
|  |    599 |               |.3.5...8.
 | 
|  |    600 |               |9724...5.""".stripMargin.replaceAll("\\n", "")
 | 
|  |    601 | 
 | 
| 57 |    602 | 
 | 
| 147 |    603 | search(game2).map(pretty)
 | 
|  |    604 | search(game3).map(pretty)
 | 
| 55 |    605 | 
 | 
|  |    606 | // for measuring time
 | 
|  |    607 | def time_needed[T](i: Int, code: => T) = {
 | 
|  |    608 |   val start = System.nanoTime()
 | 
|  |    609 |   for (j <- 1 to i) code
 | 
|  |    610 |   val end = System.nanoTime()
 | 
|  |    611 |   ((end - start) / i / 1.0e9) + " secs"
 | 
|  |    612 | }
 | 
|  |    613 | 
 | 
|  |    614 | search(game2).map(pretty)
 | 
| 57 |    615 | search(game3).distinct.length
 | 
| 147 |    616 | time_needed(1, search(game2))
 | 
|  |    617 | time_needed(1, search(game3))
 | 
| 55 |    618 | 
 | 
| 53 |    619 | 
 | 
|  |    620 | 
 | 
|  |    621 | 
 | 
| 150 |    622 | //===================
 | 
|  |    623 | // the end for today
 |