progs/lecture2.scala
changeset 57 9b88f9e04344
parent 56 6fa91be92d0c
child 58 93a2b6e4b84c
equal deleted inserted replaced
56:6fa91be92d0c 57:9b88f9e04344
     1 // Scala Lecture 2
     1 // Scala Lecture 2
     2 //=================
     2 //=================
     3 
     3 
     4 
     4 
     5 // Implicits
       
     6 //===========
       
     7 //
       
     8 // for example adding your own methods to Strings:
       
     9 // imagine you want to increment strings, like
       
    10 //
       
    11 //     "HAL".increment
       
    12 //
       
    13 // you can avoid ugly fudges, like a MyString, by
       
    14 // using implicit conversions
       
    15 
       
    16 
       
    17 implicit class MyString(s: String) {
       
    18   def increment = for (c <- s) yield (c + 1).toChar 
       
    19 }
       
    20 
       
    21 "HAL".increment
       
    22 
       
    23 
       
    24 
       
    25 // Option type
     5 // Option type
    26 //=============
     6 //=============
    27 
     7 
    28 //in Java if something unusually happens, you return null
     8 //in Java if something unusually happens, you return null;
    29 //in Scala you use Option
     9 //in Scala you use Option
    30 //   - if the value is present, you use Some(value)
    10 //   - if the value is present, you use Some(value)
    31 //   - if no value is present, you use None
    11 //   - if no value is present, you use None
    32 
    12 
    33 
    13 
    47 
    27 
    48 for ((x, y) <- ps) yield {
    28 for ((x, y) <- ps) yield {
    49   if (y == 0) None else Some(x / y)
    29   if (y == 0) None else Some(x / y)
    50 }
    30 }
    51 
    31 
    52 // getOrElse is to set a default value
    32 // getOrElse is for setting a default value
    53 
    33 
    54 val lst = List(None, Some(1), Some(2), None, Some(3))
    34 val lst = List(None, Some(1), Some(2), None, Some(3))
    55 for (x <- lst) yield x getOrElse 0
    35 for (x <- lst) yield x.getOrElse(0)
    56 
    36 
    57 
    37 
       
    38 
       
    39 
       
    40 // error handling with option (no exceptions)
       
    41 //
       
    42 //  Try(something).getOrElse(what_to_do_in_an_exception)
       
    43 //
    58 import scala.util._
    44 import scala.util._
    59 import io.Source
    45 import io.Source
    60 // error handling with option
    46 
    61 //
    47 Source.fromURL("""http://www.inf.kcl.ac.uk/staff/urbanc/""").mkString
    62 //  Try(something).getOrElse(what_to_do_in_an_exception)
       
    63 
       
    64 Source.fromURL("""http://www.inf.kcl.ac.uk/staff/urbanccc/""").mkString
       
    65 
    48 
    66 Try(Source.fromURL("""http://www.inf.kcl.ac.uk/staff/urbanc/""").mkString).getOrElse("")
    49 Try(Source.fromURL("""http://www.inf.kcl.ac.uk/staff/urbanc/""").mkString).getOrElse("")
    67 
    50 
    68 Try(Some(Source.fromURL("""http://www.inf.kcl.ac.uk/staff/urbanc/""").mkString)).getOrElse(None)
    51 Try(Some(Source.fromURL("""http://www.inf.kcl.ac.uk/staff/urbanc/""").mkString)).getOrElse(None)
    69 
    52 
    70 
    53 // a function that turns strings into numbers
    71 Integer.parseInt("12u34")
    54 Integer.parseInt("12u34")
    72 
    55 
    73 def get_me_an_int(s: String): Option[Int] = 
    56 def get_me_an_int(s: String): Option[Int] = 
    74  Try(Some(Integer.parseInt(s))).getOrElse(None)
    57  Try(Some(Integer.parseInt(s))).getOrElse(None)
    75 
    58 
    84 // see the value, you have to put yourself in the shoes of the
    67 // see the value, you have to put yourself in the shoes of the
    85 // consumer of the get_me_an_int function, and imagine you didn't
    68 // consumer of the get_me_an_int function, and imagine you didn't
    86 // write that function.
    69 // write that function.
    87 //
    70 //
    88 // In Java, if you didn't write this function, you'd have to depend on
    71 // In Java, if you didn't write this function, you'd have to depend on
    89 // the Javadoc of the get_me_an_int. If you didn't look at the Javadoc
    72 // the Javadoc of the get_me_an_int. If you didn't look at the Javadoc, 
    90 // for the Java, you might not know that get_me_an_int could return a
    73 // you might not know that get_me_an_int could return a null, and your 
    91 // null, and your code could potentially throw a NullPointerException.
    74 // code could potentially throw a NullPointerException.
    92 
       
    93 
    75 
    94 
    76 
    95 
    77 
    96 // Type abbreviations
    78 // Type abbreviations
    97 //====================
    79 //====================
    99 // some syntactic convenience
    81 // some syntactic convenience
   100 type Pos = (int, Int)
    82 type Pos = (int, Int)
   101 
    83 
   102 type Board = List[List[Int]]
    84 type Board = List[List[Int]]
   103 
    85 
       
    86 
       
    87 
       
    88 // Implicits
       
    89 //===========
       
    90 //
       
    91 // for example adding your own methods to Strings:
       
    92 // imagine you want to increment strings, like
       
    93 //
       
    94 //     "HAL".increment
       
    95 //
       
    96 // you can avoid ugly fudges, like a MyString, by
       
    97 // using implicit conversions
       
    98 
       
    99 
       
   100 implicit class MyString(s: String) {
       
   101   def increment = for (c <- s) yield (c + 1).toChar 
       
   102 }
       
   103 
       
   104 "HAL".increment
   104 
   105 
   105 
   106 
   106 // No return in Scala
   107 // No return in Scala
   107 //====================
   108 //====================
   108 
   109 
   118 def sumq(ls: List[Int]): Int = {
   119 def sumq(ls: List[Int]): Int = {
   119   (for (x <- ls) yield (return x * x)).sum[Int]
   120   (for (x <- ls) yield (return x * x)).sum[Int]
   120 }
   121 }
   121 
   122 
   122 sumq(List(1,2,3,4))
   123 sumq(List(1,2,3,4))
       
   124 
   123 
   125 
   124 // last expression in a function is the return statement
   126 // last expression in a function is the return statement
   125 def square(x: Int): Int = {
   127 def square(x: Int): Int = {
   126   println(s"The argument is ${x}.")
   128   println(s"The argument is ${x}.")
   127   x * x
   129   x * x
   153 def my_flatten(xs: List[Option[Int]]): List[Int] = {
   155 def my_flatten(xs: List[Option[Int]]): List[Int] = {
   154   ...
   156   ...
   155 }
   157 }
   156 
   158 
   157 
   159 
       
   160 
       
   161 
       
   162 
       
   163 
       
   164 
       
   165 
       
   166 
   158 def my_flatten(lst: List[Option[Int]]): List[Int] = lst match {
   167 def my_flatten(lst: List[Option[Int]]): List[Int] = lst match {
   159   case Nil => Nil
   168   case Nil => Nil
   160   case None::xs => my_flatten(xs)
   169   case None::xs => my_flatten(xs)
   161   case Some(n)::xs => n::my_flatten(xs)
   170   case Some(n)::xs => n::my_flatten(xs)
   162 }
   171 }
   168   case 1 => "one"
   177   case 1 => "one"
   169   case 2 => "two"
   178   case 2 => "two"
   170   case _ => "many"
   179   case _ => "many"
   171 }
   180 }
   172 
   181 
       
   182 get_me_a_string(0)
       
   183 
   173 // User-defined Datatypes
   184 // User-defined Datatypes
   174 //========================
   185 //========================
   175 
   186 
   176 abstract class Tree
   187 abstract class Tree
   177 case class Node(elem: Int, left: Tree, right: Tree) extends Tree
   188 case class Node(elem: Int, left: Tree, right: Tree) extends Tree
   178 case class Leaf() extends Tree
   189 case class Leaf() extends Tree
       
   190 
   179 
   191 
   180 def insert(tr: Tree, n: Int): Tree = tr match {
   192 def insert(tr: Tree, n: Int): Tree = tr match {
   181   case Leaf() => Node(n, Leaf(), Leaf())
   193   case Leaf() => Node(n, Leaf(), Leaf())
   182   case Node(m, left, right) => 
   194   case Node(m, left, right) => 
   183     if (n == m) Node(m, left, right) 
   195     if (n == m) Node(m, left, right) 
   221   case (Clown(), Peasant(_)) => true
   233   case (Clown(), Peasant(_)) => true
   222   case _ => false
   234   case _ => false
   223 }
   235 }
   224 
   236 
   225 val people = List(Knight("David"), 
   237 val people = List(Knight("David"), 
   226                   Peer("Duke", "Norfolk", 42), 
   238                   Peer("Duke", "Norfolk", 84), 
   227                   Peasant("Christian"), 
   239                   Peasant("Christian"), 
   228                   King(), 
   240                   King(), 
   229                   Clown())
   241                   Clown())
   230 
   242 
   231 println(people.sortWith(superior(_, _)))
   243 println(people.sortWith(superior(_, _)).mkString(", "))
       
   244 
       
   245 
   232 
   246 
   233 // Higher-Order Functions
   247 // Higher-Order Functions
   234 //========================
   248 //========================
   235 
   249 
   236 // functions can take functions as arguments
   250 // functions can take functions as arguments
   256 
   270 
   257 // in my collatz.scala
   271 // in my collatz.scala
   258 //(1 to bnd).map(i => (collatz(i), i)).maxBy(_._1)
   272 //(1 to bnd).map(i => (collatz(i), i)).maxBy(_._1)
   259 
   273 
   260 
   274 
   261 // type of functions
   275 // type of functions, for example f: Int => Int
       
   276 
   262 def my_map_int(lst: List[Int], f: Int => Int): List[Int] = lst match {
   277 def my_map_int(lst: List[Int], f: Int => Int): List[Int] = lst match {
   263   case Nil => Nil
   278   case Nil => Nil
   264   case x::xs => f(x)::my_map_int(xs, f)
   279   case x::xs => f(x)::my_map_int(xs, f)
   265 }
   280 }
   266 
   281 
   267 my_map_int(lst, square)
   282 my_map_int(lst, square)
   268 
   283 
       
   284 // other function types
       
   285 //
       
   286 // f1: (Int, Int) => Int
       
   287 // f2: List[String] => Option[Int]
       
   288 // ... 
       
   289 
   269 
   290 
   270 def sumOf(f: Int => Int, lst: List[Int]): Int = lst match {
   291 def sumOf(f: Int => Int, lst: List[Int]): Int = lst match {
   271   case Nil => 0
   292   case Nil => 0
   272   case x::xs => f(x) + sumOf(f, xs)
   293   case x::xs => f(x) + sumOf(f, xs)
   273 }
   294 }
   276 def sum_cubes(lst: List[Int])   = sumOf(x => x * x * x, lst)
   297 def sum_cubes(lst: List[Int])   = sumOf(x => x * x * x, lst)
   277 
   298 
   278 sum_squares(lst)
   299 sum_squares(lst)
   279 sum_cubes(lst)
   300 sum_cubes(lst)
   280 
   301 
   281 
   302 def fact(n: Int): Int = 
       
   303   if (n == 0) 1 else n * fact(n - 1)
       
   304 
       
   305 def sum_fact(lst: List[Int]) = sumOf(fact, lst)
       
   306 sum_fact(lst)
   282 
   307 
   283 // Avoid being mutable
   308 // Avoid being mutable
   284 //=====================
   309 //=====================
   285 
   310 
   286 // a student showed me...
   311 // a student showed me...
   305 (1 to 1000000000).filter(_ % 2 == 0).take(10).toList
   330 (1 to 1000000000).filter(_ % 2 == 0).take(10).toList
   306 // ==> java.lang.OutOfMemoryError: GC overhead limit exceeded
   331 // ==> java.lang.OutOfMemoryError: GC overhead limit exceeded
   307 
   332 
   308 (1 to 1000000000).view.filter(_ % 2 == 0).take(10).toList
   333 (1 to 1000000000).view.filter(_ % 2 == 0).take(10).toList
   309 
   334 
       
   335 
       
   336 
   310 // Sudoku
   337 // Sudoku
   311 //========
   338 //========
       
   339 
       
   340 // THE POINT OF THIS CODE IS NOT TO BE SUPER
       
   341 // EFFICIENT AND FAST, just explaining exhaustive
       
   342 // depth-first search
       
   343 
   312 
   344 
   313 val game0 = """.14.6.3..
   345 val game0 = """.14.6.3..
   314               |62...4..9
   346               |62...4..9
   315               |.8..5.6..
   347               |.8..5.6..
   316               |.6.2....3
   348               |.6.2....3
   318               |5....9.6.
   350               |5....9.6.
   319               |..6.2..3.
   351               |..6.2..3.
   320               |1..5...92
   352               |1..5...92
   321               |..7.9.41.""".stripMargin.replaceAll("\\n", "")
   353               |..7.9.41.""".stripMargin.replaceAll("\\n", "")
   322 
   354 
   323 
       
   324 type Pos = (Int, Int)
   355 type Pos = (Int, Int)
   325 val EmptyValue = '.'
   356 val EmptyValue = '.'
   326 val MaxValue = 9
   357 val MaxValue = 9
   327 
   358 
   328 val allValues = "123456789".toList
   359 val allValues = "123456789".toList
   329 val indexes = (0 to 8).toList
   360 val indexes = (0 to 8).toList
   330 
   361 
       
   362 
       
   363 
       
   364 
   331 def empty(game: String) = game.indexOf(EmptyValue)
   365 def empty(game: String) = game.indexOf(EmptyValue)
       
   366 def isDone(game: String) = empty(game) == -1 
   332 def emptyPosition(game: String) = (empty(game) % MaxValue, empty(game) / MaxValue)
   367 def emptyPosition(game: String) = (empty(game) % MaxValue, empty(game) / MaxValue)
   333 def isDone(game: String) = empty(game) == -1 
   368 
   334 
   369 
   335 def row(game: String, y: Int): List[Char] = indexes.map(col => game(y * MaxValue + col))
   370 def get_row(game: String, y: Int) = indexes.map(col => game(y * MaxValue + col))
   336 def col(game: String, x: Int): List[Char] = indexes.map(row => game(x + row * MaxValue))
   371 def get_col(game: String, x: Int) = indexes.map(row => game(x + row * MaxValue))
   337 
   372 
   338 def box(game: String, pos: Pos): List[Char] = {
   373 def get_box(game: String, pos: Pos): List[Char] = {
   339     def base(p: Int): Int = (p / 3) * 3
   374     def base(p: Int): Int = (p / 3) * 3
   340     val x0 = base(pos._1)
   375     val x0 = base(pos._1)
   341     val y0 = base(pos._2)
   376     val y0 = base(pos._2)
   342     val ys = (y0 until y0 + 3).toList
   377     val ys = (y0 until y0 + 3).toList
   343     (x0 until x0 + 3).toList.flatMap(x => ys.map(y => game(x + y * MaxValue)))
   378     (x0 until x0 + 3).toList.flatMap(x => ys.map(y => game(x + y * MaxValue)))
   344 }
   379 }
   345 
   380 
   346 
   381 
   347 //row(game0, 0)
   382 //get_row(game0, 0)
   348 //row(game0, 1)
   383 //get_row(game0, 1)
   349 //box(game0, (3,1))
   384 //get_box(game0, (3,1))
   350 
   385 
   351 def update(game: String, pos: Int, value: Char): String = game.updated(pos, value)
   386 def update(game: String, pos: Int, value: Char): String = game.updated(pos, value)
   352 
   387 
   353 def toAvoid(game: String, pos: Pos): List[Char] = 
   388 def toAvoid(game: String, pos: Pos): List[Char] = 
   354   (col(game, pos._1) ++ row(game, pos._2) ++ box(game, pos)).distinct
   389   (get_col(game, pos._1) ++ get_row(game, pos._2) ++ get_box(game, pos))
   355 
   390 
   356 def candidates(game: String, pos: Pos): List[Char] = allValues diff toAvoid(game,pos)
   391 def candidates(game: String, pos: Pos): List[Char] = allValues diff toAvoid(game,pos)
   357 
   392 
   358 //candidates(game0, (0,0))
   393 //candidates(game0, (0,0))
   359 
   394 
   360 def pretty(game: String): String = "\n" + (game sliding (MaxValue, MaxValue) mkString "\n")
   395 def pretty(game: String): String = "\n" + (game sliding (MaxValue, MaxValue) mkString "\n")
   361 
   396 
   362 def search(game: String): List[String] = {
   397 def search(game: String): List[String] = {
   363   if (isDone(game)) List(game)
   398   if (isDone(game)) List(game)
   364   else 
   399   else 
   365     candidates(game, emptyPosition(game)).par.map(c => search(update(game, empty(game), c))).toList.flatten
   400     candidates(game, emptyPosition(game)).map(c => search(update(game, empty(game), c))).toList.flatten
   366 }
   401 }
   367 
   402 
   368 
   403 
   369 val game1 = """23.915...
   404 val game1 = """23.915...
   370               |...2..54.
   405               |...2..54.
   373               |89.5.3.17
   408               |89.5.3.17
   374               |5.....6..
   409               |5.....6..
   375               |......9.5
   410               |......9.5
   376               |.16..7...
   411               |.16..7...
   377               |...329..1""".stripMargin.replaceAll("\\n", "")
   412               |...329..1""".stripMargin.replaceAll("\\n", "")
       
   413 
   378 
   414 
   379 // game that is in the hard category
   415 // game that is in the hard category
   380 val game2 = """8........
   416 val game2 = """8........
   381               |..36.....
   417               |..36.....
   382               |.7..9.2..
   418               |.7..9.2..
   396               |...3....6
   432               |...3....6
   397               |.......7.
   433               |.......7.
   398               |.3.5...8.
   434               |.3.5...8.
   399               |9724...5.""".stripMargin.replaceAll("\\n", "")
   435               |9724...5.""".stripMargin.replaceAll("\\n", "")
   400 
   436 
       
   437 
   401 search(game0).map(pretty)
   438 search(game0).map(pretty)
   402 search(game1).map(pretty)
   439 search(game1).map(pretty)
   403 
   440 
   404 // for measuring time
   441 // for measuring time
   405 def time_needed[T](i: Int, code: => T) = {
   442 def time_needed[T](i: Int, code: => T) = {
   408   val end = System.nanoTime()
   445   val end = System.nanoTime()
   409   ((end - start) / i / 1.0e9) + " secs"
   446   ((end - start) / i / 1.0e9) + " secs"
   410 }
   447 }
   411 
   448 
   412 search(game2).map(pretty)
   449 search(game2).map(pretty)
   413 search(game3).distinct.map(pretty).length
   450 search(game3).distinct.length
   414 time_needed(3, search(game2))
   451 time_needed(3, search(game2))
   415 time_needed(3, search(game3))
   452 time_needed(3, search(game3))
   416 
   453 
   417 
   454 
   418 
   455