progs/lecture2.scala
changeset 150 9a2f2a1de42b
parent 148 ead6089209ba
child 155 371acb50643d
equal deleted inserted replaced
149:3a6f51bc6121 150:9a2f2a1de42b
    37 for (i <- (0 until lst.length).toList) yield square(lst(i))
    37 for (i <- (0 until lst.length).toList) yield square(lst(i))
    38 
    38 
    39 // this is just so prone to off-by-one errors;
    39 // this is just so prone to off-by-one errors;
    40 // write instead
    40 // write instead
    41 
    41 
    42 for (e <- lst) yield square(e)
    42 for (e <- lst; if (e % 2) == 0; if (e != 4)) yield square(e)
    43 
    43 
    44 
    44 
    45 //this works for sets as well
    45 //this works for sets as well
    46 val st = Set(1, 2, 3, 4, 5, 6, 7, 8, 9)
    46 val st = Set(1, 2, 3, 4, 5, 6, 7, 8, 9)
    47 
    47 
    53 
    53 
    54 // Side-Effects
    54 // Side-Effects
    55 //==============
    55 //==============
    56 
    56 
    57 // with only a side-effect (no list is produced),
    57 // with only a side-effect (no list is produced),
    58 // has no "yield"
    58 // for has no "yield"
    59 
    59 
    60 for (n <- (1 to 10)) println(n)
    60 for (n <- (1 to 10)) println(n)
    61 
    61 
    62 
    62 
    63 for (n <- (1 to 10)) {
    63 for (n <- (1 to 10)) {
    69 
    69 
    70 
    70 
    71 
    71 
    72 // know when to use yield and when not:
    72 // know when to use yield and when not:
    73 
    73 
    74 for (e <- Set(1, 2, 3, 4, 5, 6, 7, 8, 9); if e < 5) yield square(e)
    74 val test = 
       
    75  for (e <- Set(1, 2, 3, 4, 5, 6, 7, 8, 9); if e < 5) yield square(e)
       
    76 
    75 
    77 
    76 
    78 
    77 // Option type
    79 // Option type
    78 //=============
    80 //=============
    79 
    81 
    81 //in Scala you use Option
    83 //in Scala you use Option
    82 //   - if the value is present, you use Some(value)
    84 //   - if the value is present, you use Some(value)
    83 //   - if no value is present, you use None
    85 //   - if no value is present, you use None
    84 
    86 
    85 
    87 
    86 List(7,2,3,4,5,6).find(_ < 4)
    88 List(7,24,3,4,5,6).find(_ < 4)
    87 List(5,6,7,8,9).find(_ < 4)
    89 List(5,6,7,8,9).find(_ < 4)
    88 
    90 
       
    91 List(7,2,3,4,5,6).filter(_ < 4)
    89 
    92 
    90 // some operations on Option's
    93 // some operations on Option's
    91 
    94 
    92 val lst = List(None, Some(1), Some(2), None, Some(3))
    95 val lst = List(None, Some(1), Some(2), None, Some(3))
    93 
    96 
    94 lst.flatten
    97 lst.flatten
    95 
    98 
    96 Some(1).get
    99 Some(10).get
       
   100 None.get
    97 
   101 
    98 Some(1).isDefined
   102 Some(1).isDefined
    99 None.isDefined
   103 None.isDefined
   100 
   104 
   101 val ps = List((3, 0), (3, 2), (4, 2), (2, 0), (1, 0), (1, 1))
   105 val ps = List((3, 0), (3, 2), (4, 2), (2, 0), (1, 0), (1, 1))
   129 
   133 
   130 
   134 
   131 import io.Source
   135 import io.Source
   132 
   136 
   133 val my_url = """https://nms.kcl.ac.uk/christian.urban"""
   137 val my_url = """https://nms.kcl.ac.uk/christian.urban"""
       
   138 //val my_url = """https://nms.kcl.ac.uk/christan.urban"""  // misspelled
   134 
   139 
   135 Source.fromURL(my_url).mkString
   140 Source.fromURL(my_url).mkString
   136 
   141 
   137 Try(Source.fromURL(my_url).mkString).getOrElse("")
   142 Try(Source.fromURL(my_url).mkString).getOrElse("")
   138 
   143 
   165 // code could potentially throw a NullPointerException.
   170 // code could potentially throw a NullPointerException.
   166 
   171 
   167 
   172 
   168 // even Scala is not immune to problems like this:
   173 // even Scala is not immune to problems like this:
   169 
   174 
   170 List(5,6,7,8,9).indexOf(7)
   175 List(5,6,7,8,9).indexOf(42)
   171 
   176 
   172 
   177 
       
   178 // ... how are we supposed to know that this returns -1
   173 
   179 
   174 
   180 
   175 // Higher-Order Functions
   181 // Higher-Order Functions
   176 //========================
   182 //========================
   177 
   183 
   180 val lst = (1 to 10).toList
   186 val lst = (1 to 10).toList
   181 
   187 
   182 def even(x: Int) : Boolean = x % 2 == 0
   188 def even(x: Int) : Boolean = x % 2 == 0
   183 def odd(x: Int) : Boolean = x % 2 == 1
   189 def odd(x: Int) : Boolean = x % 2 == 1
   184 
   190 
   185 lst.filter(x => even(x))
   191 lst.filter(x => even(x) && odd(x))
   186 lst.filter(even(_))
   192 lst.filter(even(_))
   187 lst.filter(even)
   193 lst.filter(odd && even)
   188 
   194 
   189 lst.find(_ > 8)
   195 lst.find(_ > 8)
   190 
   196 
   191 // map applies a function to each element of a list
   197 // map applies a function to each element of a list
   192 
   198 
   193 def square(x: Int): Int = x * x
   199 def square(x: Int): Int = x * x
   194 
   200 
       
   201 val lst = (1 to 10).toList
   195 lst.map(square)
   202 lst.map(square)
   196 
   203 
   197 lst.map(square).filter(_ > 4)
   204 lst.map(square).filter(_ > 4)
   198 
   205 
   199 lst.map(square).filter(_ > 4).map(square)
   206 lst.map(square).filter(_ > 4).map(square)
   200 
   207 
   201 // map works for most collection types, including sets
   208 // map works for most collection types, including sets
   202 Set(1, 3, 6).map(square)
   209 Set(1, 3, 6).map(square).filter(_ > 4)
   203 
   210 
   204 
   211 
   205 // Why could functions as arguments be useful?
   212 val l = List((1, 3),(2, 4),(4, 1),(6, 2))
       
   213 
       
   214 l.map(square(_._1))
       
   215 
       
   216 
       
   217 // Why are functions as arguments useful?
   206 //
   218 //
   207 // Consider the sum between a and b:
   219 // Consider the sum between a and b:
   208 
   220 
   209 def sumInts(a: Int, b: Int) : Int = 
   221 def sumInts(a: Int, b: Int) : Int = 
   210   if (a > b) 0 else a + sumInts(a + 1, b)
   222   if (a > b) 0 else a + sumInts(a + 1, b)
   211 
   223 
   212 
   224 
   213 sumInt(10, 16)
   225 sumInts(10, 16)
   214 
   226 
   215 // sum squares
   227 // sum squares
   216 def square(n: Int) : Int = n * n
   228 def square(n: Int) : Int = n * n
   217 
   229 
   218 def sumSquares(a: Int, b: Int) : Int = 
   230 def sumSquares(a: Int, b: Int) : Int = 
   230 
   242 
   231 sumFacts(2, 6)
   243 sumFacts(2, 6)
   232 
   244 
   233 
   245 
   234 
   246 
   235 // You can see the pattern....can we simplify out work?
   247 // You can see the pattern....can we simplify our work?
   236 // The type of functions from ints to ints: Int => Int
   248 // The type of functions from ints to ints: Int => Int
   237 
   249 
   238 def sum(f: Int => Int, a: Int, b: Int) : Int = {
   250 def sum(f: Int => Int, a: Int, b: Int) : Int = {
   239   if (a > b) 0 
   251   if (a > b) 0 
   240   else f(a) + sum(f, a + 1, b)
   252   else f(a) + sum(f, a + 1, b)
   247 // What should we do for sumInts?
   259 // What should we do for sumInts?
   248 
   260 
   249 def id(n: Int) : Int = n
   261 def id(n: Int) : Int = n
   250 def sumInts(a: Int, b: Int) : Int = sum(id, a, b)
   262 def sumInts(a: Int, b: Int) : Int = sum(id, a, b)
   251 
   263 
       
   264 sumInts(10, 12)
   252 
   265 
   253 
   266 
   254 // Anonymous Functions: You can also write:
   267 // Anonymous Functions: You can also write:
   255 
   268 
   256 def sumCubes(a: Int, b: Int) : Int =   sum(x => x * x * x, a, b)
   269 def sumCubes(a: Int, b: Int) : Int =   sum(x => x * x * x, a, b)
   266 
   279 
   267 
   280 
   268 // an aside: partial application
   281 // an aside: partial application
   269 
   282 
   270 def add(a: Int)(b: Int) : Int = a + b
   283 def add(a: Int)(b: Int) : Int = a + b
       
   284 def add_abc(a: Int)(b: Int)(c: Int) : Int = a + b + c
       
   285 
       
   286 val add2 : Int => Int = add(2)
       
   287 add2(5)
       
   288 
       
   289 val add2_bc : Int => Int => Int = add_abc(2) 
       
   290 val add2_9_c : Int => Int = add2_bc(9) 
       
   291 
       
   292 add2_9_c(10)
   271 
   293 
   272 sum(add(2), 0, 2)
   294 sum(add(2), 0, 2)
   273 sum(add(10), 0, 2)
   295 sum(add(10), 0, 2)
   274 
   296 
   275 def add2(a: Int, b: Int) : Int = a + b
       
   276 sum(x => add2(2, x), 0, 2)
       
   277 sum(x => add2(10, x), 0, 2)
       
   278 
   297 
   279 // Function Composition
   298 // Function Composition
   280 //======================
   299 //======================
   281 
   300 
   282 // How could Higher-Order Functions and Options be helpful?
   301 // How can be Higher-Order Functions and Options be helpful?
   283 
   302 
   284 def add_footer(msg: String) : String = msg ++ " - Sent from iOS"
   303 def add_footer(msg: String) : String = msg ++ " - Sent from iOS"
   285 
   304 
   286 def valid_msg(msg: String) : Boolean = msg.size <= 140
   305 def valid_msg(msg: String) : Boolean = msg.size <= 140
   287 
   306 
   288 def duplicate(s: String) : String = s ++ s
   307 def duplicate(s: String) : String = s ++ s
   289 
   308 
   290 // they compose nicely
   309 // they compose very nicely, e.g
       
   310 
   291 valid_msg(add_footer("Hello World"))
   311 valid_msg(add_footer("Hello World"))
   292 valid_msg(duplicate(add_footer("Hello World")))
   312 valid_msg(duplicate(duplicate(add_footer("Helloooooooooooooooooo World"))))
   293 
   313 
   294 
   314 // but not all functions do
   295 // first_word: let's first do it the ugly Java way using null:
   315 // first_word: let's first do it the ugly Java way using null:
   296 
   316 
   297 def first_word(msg: String) : String = {
   317 def first_word(msg: String) : String = {
   298   val words = msg.split(" ")
   318   val words = msg.split(" ")
   299   if (words(0) != "") words(0) else null
   319   if (words(0) != "") words(0) else null
   304 
   324 
   305 def extended_duplicate(s: String) : String = 
   325 def extended_duplicate(s: String) : String = 
   306   if (s != null) s ++ s else null
   326   if (s != null) s ++ s else null
   307 
   327 
   308 extended_duplicate(first_word(""))
   328 extended_duplicate(first_word(""))
       
   329 
       
   330 // but this is against the rules of the game: we do not want
       
   331 // to change duplicate, because first_word might return null
   309 
   332 
   310 
   333 
   311 // Avoid always null!
   334 // Avoid always null!
   312 def better_first_word(msg: String) : Option[String] = {
   335 def better_first_word(msg: String) : Option[String] = {
   313   val words = msg.split(" ")
   336   val words = msg.split(" ")
   314   if (words(0) != "") Some(words(0)) else None
   337   if (words(0) != "") Some(words(0)) else None
   315 }
   338 }
   316 
   339 
   317 better_first_word("Hello World").map(duplicate)
   340 better_first_word("Hello World").map(duplicate)
   318 better_first_word("Hello World").map(duplicate).map(duplicate).map(valid_msg)
   341 
       
   342 better_first_word("Hello World").map(duplicate)
       
   343 better_first_word("").map(duplicate).map(duplicate).map(valid_msg)
   319 
   344 
   320 better_first_word("").map(duplicate)
   345 better_first_word("").map(duplicate)
   321 better_first_word("").map(duplicate).map(valid_msg)
   346 better_first_word("").map(duplicate).map(valid_msg)
   322 
       
   323 
       
   324 
       
   325 
       
   326 
       
   327 
       
   328 // Implicits (Cool Feature)
       
   329 //=========================
       
   330 //
       
   331 // For example adding your own methods to Strings:
       
   332 // Imagine you want to increment strings, like
       
   333 //
       
   334 //     "HAL".increment
       
   335 //
       
   336 // you can avoid ugly fudges, like a MyString, by
       
   337 // using implicit conversions.
       
   338 
       
   339 
       
   340 implicit class MyString(s: String) {
       
   341   def increment = for (c <- s) yield (c + 1).toChar 
       
   342 }
       
   343 
       
   344 "HAL".increment
       
   345 
       
   346 
       
   347 
       
   348 // No returns in Scala
       
   349 //====================
       
   350 
       
   351 // You should not use "return" in Scala:
       
   352 //
       
   353 // A return expression, when evaluated, abandons the 
       
   354 // current computation and returns to the caller of the 
       
   355 // function in which return appears."
       
   356 
       
   357 def sq1(x: Int): Int = x * x
       
   358 def sq2(x: Int): Int = return x * x
       
   359 
       
   360 def sumq(ls: List[Int]): Int = {
       
   361   ls.map(sq1).sum[Int]
       
   362 }
       
   363 
       
   364 sumq(List(1, 2, 3, 4))
       
   365 
       
   366 
       
   367 
       
   368 def sumq(ls: List[Int]): Int = {
       
   369   val sqs : List[Int] = for (x <- ls) yield (return x * x)
       
   370   sqs.sum
       
   371 }
       
   372 
       
   373 
       
   374 
   347 
   375 
   348 
   376 // Pattern Matching
   349 // Pattern Matching
   377 //==================
   350 //==================
   378 
   351 
   393 // remember
   366 // remember
   394 val lst = List(None, Some(1), Some(2), None, Some(3)).flatten
   367 val lst = List(None, Some(1), Some(2), None, Some(3)).flatten
   395 
   368 
   396 
   369 
   397 def my_flatten(xs: List[Option[Int]]): List[Int] = {
   370 def my_flatten(xs: List[Option[Int]]): List[Int] = {
   398   ...
   371   ...?
   399 }
   372 }
   400 
   373 
   401 
   374 
   402 
   375 
   403 
   376 
   407   case None::xs => my_flatten(xs)
   380   case None::xs => my_flatten(xs)
   408   case Some(n)::xs => n::my_flatten(xs)
   381   case Some(n)::xs => n::my_flatten(xs)
   409 }
   382 }
   410 
   383 
   411 
   384 
   412 // another example
   385 // another example including a catch-all pattern
   413 def get_me_a_string(n: Int): String = n match {
   386 def get_me_a_string(n: Int): String = n match {
   414   case 0 => "zero"
   387   case 0 => "zero"
   415   case 1 => "one"
   388   case 1 => "one"
   416   case 2 => "two"
   389   case 2 => "two"
   417   case _ => "many"
   390   case _ => "many"
   447   case Green() => true
   420   case Green() => true
   448   case Blue()  => false 
   421   case Blue()  => false 
   449 }
   422 }
   450 
   423 
   451 
   424 
   452 // actually this can be written with "object"
   425 // actually colors can be written with "object",
       
   426 // because they do not take any arguments
   453 
   427 
   454 
   428 
   455 // another example
   429 // another example
   456 //=================
   430 //=================
       
   431 
       
   432 // Once upon a time, in a complete fictional country there were persons...
   457 
   433 
   458 abstract class Person
   434 abstract class Person
   459 case class King() extends Person
   435 case class King() extends Person
   460 case class Peer(deg: String, terr: String, succ: Int) extends Person
   436 case class Peer(deg: String, terr: String, succ: Int) extends Person
   461 case class Knight(name: String) extends Person
   437 case class Knight(name: String) extends Person
   491 
   467 
   492 
   468 
   493 
   469 
   494 
   470 
   495 
   471 
   496 
       
   497 // Problems with mutability and parallel computations
   472 // Problems with mutability and parallel computations
   498 //====================================================
   473 //====================================================
   499 
   474 
   500 def count_intersection(A: Set[Int], B: Set[Int]) : Int = {
   475 def count_intersection(A: Set[Int], B: Set[Int]) : Int = {
   501   var count = 0
   476   var count = 0
   506 val A = (1 to 1000).toSet
   481 val A = (1 to 1000).toSet
   507 val B = (1 to 1000 by 4).toSet
   482 val B = (1 to 1000 by 4).toSet
   508 
   483 
   509 count_intersection(A, B)
   484 count_intersection(A, B)
   510 
   485 
   511 // but do not try to add .par to the for-loop above
   486 // but do not try to add .par to the for-loop above,
       
   487 // otherwise you will be caught in race-condition hell.
   512 
   488 
   513 
   489 
   514 //propper parallel version
   490 //propper parallel version
   515 def count_intersection2(A: Set[Int], B: Set[Int]) : Int = 
   491 def count_intersection2(A: Set[Int], B: Set[Int]) : Int = 
   516   A.par.count(x => B contains x)
   492   A.par.count(x => B contains x)
   531 
   507 
   532 time_needed(10, count_intersection(A, B))
   508 time_needed(10, count_intersection(A, B))
   533 time_needed(10, count_intersection2(A, B))
   509 time_needed(10, count_intersection2(A, B))
   534 
   510 
   535 
   511 
       
   512 // Implicits (Cool Feature)
       
   513 //=========================
       
   514 //
       
   515 // For example adding your own methods to Strings:
       
   516 // Imagine you want to increment strings, like
       
   517 //
       
   518 //     "HAL".increment
       
   519 //
       
   520 // you can avoid ugly fudges, like a MyString, by
       
   521 // using implicit conversions.
       
   522 
       
   523 
       
   524 implicit class MyString(s: String) {
       
   525   def increment = for (c <- s) yield (c + 1).toChar 
       
   526 }
       
   527 
       
   528 "HAL".increment
       
   529 
       
   530 
       
   531 
       
   532 // No returns in Scala
       
   533 //====================
       
   534 
       
   535 // You should not use "return" in Scala:
       
   536 //
       
   537 // A return expression, when evaluated, abandons the 
       
   538 // current computation and returns to the caller of the 
       
   539 // function in which return appears."
       
   540 
       
   541 def sq1(x: Int): Int = x * x
       
   542 def sq2(x: Int): Int = return x * x
       
   543 
       
   544 def sumq(ls: List[Int]): Int = {
       
   545   ls.map(sq1).sum[Int]
       
   546 }
       
   547 
       
   548 sumq(List(1, 2, 3, 4))
       
   549 
       
   550 
       
   551 
       
   552 def sumq(ls: List[Int]): Int = {
       
   553   val sqs : List[Int] = for (x <- ls) yield (return x * x)
       
   554   sqs.sum
       
   555 }
       
   556 
       
   557 
       
   558 
       
   559 
   536 // Type abbreviations
   560 // Type abbreviations
   537 //====================
   561 //====================
   538 
   562 
   539 // some syntactic convenience
   563 // some syntactic convenience
   540 
   564 
   542 type Board = List[List[Int]]
   566 type Board = List[List[Int]]
   543 
   567 
   544 
   568 
   545 
   569 
   546 
   570 
   547 // Sudoku
   571 // Sudoku in Scala
   548 //========
   572 //=================
   549 
   573 
   550 // THE POINT OF THIS CODE IS NOT TO BE SUPER
   574 // THE POINT OF THIS CODE IS NOT TO BE SUPER
   551 // EFFICIENT AND FAST, just explaining exhaustive
   575 // EFFICIENT AND FAST, just explaining exhaustive
   552 // depth-first search
   576 // depth-first search
   553 
   577 
   570 val indexes = (0 to 8).toList
   594 val indexes = (0 to 8).toList
   571 
   595 
   572 
   596 
   573 def empty(game: String) = game.indexOf(EmptyValue)
   597 def empty(game: String) = game.indexOf(EmptyValue)
   574 def isDone(game: String) = empty(game) == -1 
   598 def isDone(game: String) = empty(game) == -1 
   575 def emptyPosition(game: String) = (empty(game) % MaxValue, empty(game) / MaxValue)
   599 def emptyPosition(game: String) = 
   576 
   600   (empty(game) % MaxValue, empty(game) / MaxValue)
   577 
   601 
   578 def get_row(game: String, y: Int) = indexes.map(col => game(y * MaxValue + col))
   602 
   579 def get_col(game: String, x: Int) = indexes.map(row => game(x + row * MaxValue))
   603 def get_row(game: String, y: Int) = 
       
   604   indexes.map(col => game(y * MaxValue + col))
       
   605 def get_col(game: String, x: Int) = 
       
   606   indexes.map(row => game(x + row * MaxValue))
   580 
   607 
   581 get_row(game0, 3)
   608 get_row(game0, 3)
   582 get_col(game0, 0)
   609 get_col(game0, 0)
   583 
   610 
   584 def get_box(game: String, pos: Pos): List[Char] = {
   611 def get_box(game: String, pos: Pos): List[Char] = {
   592 get_box(game0, (0, 0))
   619 get_box(game0, (0, 0))
   593 get_box(game0, (1, 1))
   620 get_box(game0, (1, 1))
   594 get_box(game0, (2, 1))
   621 get_box(game0, (2, 1))
   595 
   622 
   596 // this is not mutable!!
   623 // this is not mutable!!
   597 def update(game: String, pos: Int, value: Char): String = game.updated(pos, value)
   624 def update(game: String, pos: Int, value: Char): String = 
       
   625   game.updated(pos, value)
   598 
   626 
   599 def toAvoid(game: String, pos: Pos): List[Char] = 
   627 def toAvoid(game: String, pos: Pos): List[Char] = 
   600   (get_col(game, pos._1) ++ get_row(game, pos._2) ++ get_box(game, pos))
   628   (get_col(game, pos._1) ++ get_row(game, pos._2) ++ get_box(game, pos))
   601 
   629 
   602 def candidates(game: String, pos: Pos): List[Char] = allValues.diff(toAvoid(game,pos))
   630 def candidates(game: String, pos: Pos): List[Char] = 
       
   631   allValues.diff(toAvoid(game,pos))
   603 
   632 
   604 //candidates(game0, (0,0))
   633 //candidates(game0, (0,0))
   605 
   634 
   606 def pretty(game: String): String = 
   635 def pretty(game: String): String = 
   607   "\n" + (game sliding (MaxValue, MaxValue) mkString "\n")
   636   "\n" + (game sliding (MaxValue, MaxValue) mkString "\n")
   608 
   637 
   609 def search(game: String): List[String] = {
   638 def search(game: String): List[String] = {
   610   if (isDone(game)) List(game)
   639   if (isDone(game)) List(game)
   611   else {
   640   else {
   612     val cs = candidates(game, emptyPosition(game))
   641     val cs = candidates(game, emptyPosition(game))
   613     cs.map(c => search(update(game, empty(game), c))).toList.flatten
   642     cs.par.map(c => search(update(game, empty(game), c))).toList.flatten
   614   }
   643   }
   615 }
   644 }
   616 
   645 
   617 search(game0).map(pretty)
   646 search(game0).map(pretty)
   618 
   647 
   668 time_needed(1, search(game3))
   697 time_needed(1, search(game3))
   669 
   698 
   670 
   699 
   671 
   700 
   672 
   701 
   673 
   702 //===================
       
   703 // the end for today