progs/lecture2.scala
changeset 55 6610c1dfa8a9
parent 53 9f8751912560
child 56 6fa91be92d0c
equal deleted inserted replaced
54:34153e0485fc 55:6610c1dfa8a9
     1 // Scala Lecture 2
     1 // Scala Lecture 2
     2 //=================
     2 //=================
       
     3 
       
     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
     3 
    22 
     4 
    23 
     5 
    24 
     6 // Option type
    25 // Option type
     7 //=============
    26 //=============
    72 // null, and your code could potentially throw a NullPointerException.
    91 // null, and your code could potentially throw a NullPointerException.
    73 
    92 
    74 
    93 
    75 
    94 
    76 
    95 
    77 
       
    78 // Type abbreviations
    96 // Type abbreviations
    79 //====================
    97 //====================
    80 
    98 
    81 // some syntactic convenience
    99 // some syntactic convenience
    82 type Pos = (int, Int)
   100 type Pos = (int, Int)
    83 
   101 
    84 type Board = List[List[Int]]
   102 type Board = List[List[Int]]
    85 
       
    86 
   103 
    87 
   104 
    88 
   105 
    89 // No return in Scala
   106 // No return in Scala
    90 //====================
   107 //====================
   107 // last expression in a function is the return statement
   124 // last expression in a function is the return statement
   108 def square(x: Int): Int = {
   125 def square(x: Int): Int = {
   109   println(s"The argument is ${x}.")
   126   println(s"The argument is ${x}.")
   110   x * x
   127   x * x
   111 }
   128 }
       
   129 
       
   130 
   112 
   131 
   113 // Pattern Matching
   132 // Pattern Matching
   114 //==================
   133 //==================
   115 
   134 
   116 // A powerful tool which is supposed to come to Java in a few years
   135 // A powerful tool which is supposed to come to Java in a few years
   149   case 1 => "one"
   168   case 1 => "one"
   150   case 2 => "two"
   169   case 2 => "two"
   151   case _ => "many"
   170   case _ => "many"
   152 }
   171 }
   153 
   172 
       
   173 // User-defined Datatypes
       
   174 //========================
       
   175 
       
   176 abstract class Tree
       
   177 case class Node(elem: Int, left: Tree, right: Tree) extends Tree
       
   178 case class Leaf() extends Tree
       
   179 
       
   180 def insert(tr: Tree, n: Int): Tree = tr match {
       
   181   case Leaf() => Node(n, Leaf(), Leaf())
       
   182   case Node(m, left, right) => 
       
   183     if (n == m) Node(m, left, right) 
       
   184     else if (n < m) Node(m, insert(left, n), right)
       
   185     else Node(m, left, insert(right, n))
       
   186 }
       
   187 
       
   188 
       
   189 val t1 = Node(4, Node(2, Leaf(), Leaf()), Node(7, Leaf(), Leaf()))
       
   190 insert(t1, 3)
       
   191 
       
   192 def balance(tr: Tree): Int = tr match {
       
   193   case Leaf() => 0
       
   194   case Node(_, left, right) => balance(left) - balance(right)
       
   195 }
       
   196 
       
   197 
       
   198 // another example
       
   199 
       
   200 abstract class Person
       
   201 case class King() extends Person
       
   202 case class Peer(deg: String, terr: String, succ: Int) extends Person
       
   203 case class Knight(name: String) extends Person
       
   204 case class Peasant(name: String) extends Person
       
   205 case class Clown() extends Person
       
   206 
       
   207 def title(p: Person): String = p match {
       
   208   case King() => "His Majesty the King"
       
   209   case Peer(deg, terr, _) => s"The ${deg} of ${terr}"
       
   210   case Knight(name) => s"Sir ${name}"
       
   211   case Peasant(name) => name
       
   212 }
       
   213 
       
   214 def superior(p1: Person, p2: Person): Boolean = (p1, p2) match {
       
   215   case (King(), _) => true
       
   216   case (Peer(_,_,_), Knight(_)) => true
       
   217   case (Peer(_,_,_), Peasant(_)) => true
       
   218   case (Peer(_,_,_), Clown()) => true
       
   219   case (Knight(_), Peasant(_)) => true
       
   220   case (Knight(_), Clown()) => true
       
   221   case (Clown(), Peasant(_)) => true
       
   222   case _ => false
       
   223 }
       
   224 
       
   225 val people = List(Knight("David"), 
       
   226                   Peer("Duke", "Norfolk", 42), 
       
   227                   Peasant("Christian"), 
       
   228                   King(), 
       
   229                   Clown())
       
   230 
       
   231 println(people.sortWith(superior(_, _)))
   154 
   232 
   155 // Higher-Order Functions
   233 // Higher-Order Functions
   156 //========================
   234 //========================
   157 
   235 
   158 // functions can take functions as arguments
   236 // functions can take functions as arguments
   172 
   250 
   173 lst.map(square)
   251 lst.map(square)
   174 
   252 
   175 lst.map(square).filter(_ > 4)
   253 lst.map(square).filter(_ > 4)
   176 
   254 
   177 
   255 lst.map(square).filter(_ > 4).map(square)
       
   256 
       
   257 // in my collatz.scala
       
   258 //(1 to bnd).map(i => (collatz(i), i)).maxBy(_._1)
       
   259 
       
   260 
       
   261 // type of functions
       
   262 def my_map_int(lst: List[Int], f: Int => Int): List[Int] = lst match {
       
   263   case Nil => Nil
       
   264   case x::xs => f(x)::my_map_int(xs, f)
       
   265 }
       
   266 
       
   267 my_map_int(lst, square)
       
   268 
       
   269 
       
   270 def sumOf(f: Int => Int, lst: List[Int]): Int = lst match {
       
   271   case Nil => 0
       
   272   case x::xs => f(x) + sumOf(f, xs)
       
   273 }
       
   274 
       
   275 def sum_squares(lst: List[Int]) = sumOf(square, lst)
       
   276 def sum_cubes(lst: List[Int])   = sumOf(x => x * x * x, lst)
       
   277 
       
   278 sum_squares(lst)
       
   279 sum_cubes(lst)
   178 
   280 
   179 
   281 
   180 // Sudoku
   282 // Sudoku
   181 //========
   283 //========
   182 
   284 
   183 
   285 val game0 = """.14.6.3..
   184 
   286               |62...4..9
   185 
   287               |.8..5.6..
   186 
   288               |.6.2....3
   187 //sorting, higher-order functions
   289               |.7..1..5.
   188 //lexicographic ordering
   290               |5....9.6.
   189 
   291               |..6.2..3.
   190 
   292               |1..5...92
   191 // Implicits
   293               |..7.9.41.""".stripMargin.replaceAll("\\n", "")
   192 //===========
   294 
   193 //
   295 
   194 // for example adding your own methods to Strings:
   296 type Pos = (Int, Int)
   195 // imagine you want to increment strings, like
   297 val EmptyValue = '.'
   196 //
   298 val MaxValue = 9
   197 //     "HAL".increment
   299 
   198 //
   300 val allValues = "123456789".toList
   199 // you can avoid ugly fudges, like a MyString, by
   301 val indexes = (0 to 8).toList
   200 // using implicit conversions
   302 
   201 
   303 def empty(game: String) = game.indexOf(EmptyValue)
   202 
   304 def emptyPosition(game: String) = (empty(game) % MaxValue, empty(game) / MaxValue)
   203 implicit class MyString(s: String) {
   305 def isDone(game: String) = empty(game) == -1 
   204   def increment = for (c <- s) yield (c + 1).toChar 
   306 
   205 }
   307 def row(game: String, y: Int): List[Char] = indexes.map(col => game(y * MaxValue + col))
   206 
   308 def col(game: String, x: Int): List[Char] = indexes.map(row => game(x + row * MaxValue))
   207 "HAL".increment
   309 
       
   310 def box(game: String, pos: Pos): List[Char] = {
       
   311     def base(p: Int): Int = (p / 3) * 3
       
   312     val x0 = base(pos._1)
       
   313     val y0 = base(pos._2)
       
   314     val ys = (y0 until y0 + 3).toList
       
   315     (x0 until x0 + 3).toList.flatMap(x => ys.map(y => game(x + y * MaxValue)))
       
   316 }
       
   317 
       
   318 
       
   319 //row(game0, 0)
       
   320 //row(game0, 1)
       
   321 //box(game0, (3,1))
       
   322 
       
   323 def update(game: String, pos: Int, value: Char): String = game.updated(pos, value)
       
   324 
       
   325 def toAvoid(game: String, pos: Pos): List[Char] = 
       
   326   (col(game, pos._1) ++ row(game, pos._2) ++ box(game, pos)).distinct
       
   327 
       
   328 def candidates(game: String, pos: Pos): List[Char] = allValues diff toAvoid(game,pos)
       
   329 
       
   330 //candidates(game0, (0,0))
       
   331 
       
   332 def pretty(game: String): String = "\n" + (game sliding (MaxValue, MaxValue) mkString "\n")
       
   333 
       
   334 def search(game: String): List[String] = {
       
   335   if (isDone(game)) List(game)
       
   336   else 
       
   337     candidates(game, emptyPosition(game)).par.map(c => search(update(game, empty(game), c))).toList.flatten
       
   338 }
       
   339 
       
   340 
       
   341 val game1 = """23.915...
       
   342               |...2..54.
       
   343               |6.7......
       
   344               |..1.....9
       
   345               |89.5.3.17
       
   346               |5.....6..
       
   347               |......9.5
       
   348               |.16..7...
       
   349               |...329..1""".stripMargin.replaceAll("\\n", "")
       
   350 
       
   351 // game that is in the hard category
       
   352 val game2 = """8........
       
   353               |..36.....
       
   354               |.7..9.2..
       
   355               |.5...7...
       
   356               |....457..
       
   357               |...1...3.
       
   358               |..1....68
       
   359               |..85...1.
       
   360               |.9....4..""".stripMargin.replaceAll("\\n", "")
       
   361 
       
   362 // game with multiple solutions
       
   363 val game3 = """.8...9743
       
   364               |.5...8.1.
       
   365               |.1.......
       
   366               |8....5...
       
   367               |...8.4...
       
   368               |...3....6
       
   369               |.......7.
       
   370               |.3.5...8.
       
   371               |9724...5.""".stripMargin.replaceAll("\\n", "")
       
   372 
       
   373 search(game0).map(pretty)
       
   374 search(game1).map(pretty)
       
   375 
       
   376 // for measuring time
       
   377 def time_needed[T](i: Int, code: => T) = {
       
   378   val start = System.nanoTime()
       
   379   for (j <- 1 to i) code
       
   380   val end = System.nanoTime()
       
   381   ((end - start) / i / 1.0e9) + " secs"
       
   382 }
       
   383 
       
   384 search(game2).map(pretty)
       
   385 search(game3).distinct.map(pretty).length
       
   386 time_needed(3, search(game2))
       
   387 time_needed(3, search(game3))
       
   388 
       
   389 
       
   390 
       
   391 
       
   392