progs/lecture4.scala
changeset 325 ca9c1cf929fa
parent 320 cdfb2ce30a3d
child 326 e5453add7df6
equal deleted inserted replaced
324:2969ee4a6cee 325:ca9c1cf929fa
     1 // Scala Lecture 4
     1 // Scala Lecture 4
     2 //=================
     2 //=================
       
     3 
       
     4 
       
     5 // expressions (essentially trees)
       
     6 
       
     7 abstract class Exp
       
     8 case class N(n: Int) extends Exp                  // for numbers
       
     9 case class Plus(e1: Exp, e2: Exp) extends Exp
       
    10 case class Times(e1: Exp, e2: Exp) extends Exp
       
    11 
       
    12 def string(e: Exp) : String = e match {
       
    13   case N(n) => s"$n"
       
    14   case Plus(e1, e2) => s"(${string(e1)} + ${string(e2)})" 
       
    15   case Times(e1, e2) => s"(${string(e1)} * ${string(e2)})"
       
    16 }
       
    17 
       
    18 val e = Plus(N(9), Times(N(3), N(4)))
       
    19 println(string(e))
       
    20 
       
    21 def eval(e: Exp) : Int = e match {
       
    22   case N(n) => n
       
    23   case Plus(e1, e2) => eval(e1) + eval(e2) 
       
    24   case Times(e1, e2) => eval(e1) * eval(e2) 
       
    25 }
       
    26 
       
    27 println(eval(e))
       
    28 
       
    29 // simplification rules:
       
    30 // e + 0, 0 + e => e 
       
    31 // e * 0, 0 * e => 0
       
    32 // e * 1, 1 * e => e
       
    33 
       
    34 def simp(e: Exp) : Exp = e match {
       
    35   case N(n) => N(n)
       
    36   case Plus(e1, e2) => (simp(e1), simp(e2)) match {
       
    37     case (N(0), e2s) => e2s
       
    38     case (e1s, N(0)) => e1s
       
    39     case (e1s, e2s) => Plus(e1s, e2s)
       
    40   }  
       
    41   case Times(e1, e2) => (simp(e1), simp(e2)) match {
       
    42     case (N(0), _) => N(0)
       
    43     case (_, N(0)) => N(0)
       
    44     case (N(1), e2s) => e2s
       
    45     case (e1s, N(1)) => e1s
       
    46     case (e1s, e2s) => Times(e1s, e2s)
       
    47   }  
       
    48 }
       
    49 
       
    50 
       
    51 val e2 = Times(Plus(N(0), N(1)), Plus(N(0), N(9)))
       
    52 println(string(e2))
       
    53 println(string(simp(e2)))
       
    54 
       
    55 
       
    56 // Tokens and Reverse Polish Notation
       
    57 abstract class Token
       
    58 case class T(n: Int) extends Token
       
    59 case object PL extends Token
       
    60 case object TI extends Token
       
    61 
       
    62 // transfroming an Exp into a list of tokens
       
    63 def rp(e: Exp) : List[Token] = e match {
       
    64   case N(n) => List(T(n))
       
    65   case Plus(e1, e2) => rp(e1) ::: rp(e2) ::: List(PL) 
       
    66   case Times(e1, e2) => rp(e1) ::: rp(e2) ::: List(TI) 
       
    67 }
       
    68 println(string(e2))
       
    69 println(rp(e2))
       
    70 
       
    71 def comp(ls: List[Token], st: List[Int]) : Int = (ls, st) match {
       
    72   case (Nil, st) => st.head 
       
    73   case (T(n)::rest, st) => comp(rest, n::st)
       
    74   case (PL::rest, n1::n2::st) => comp(rest, n1 + n2::st)
       
    75   case (TI::rest, n1::n2::st) => comp(rest, n1 * n2::st)
       
    76 }
       
    77 
       
    78 comp(rp(e), Nil)
       
    79 
       
    80 def proc(s: String) : Token = s match {
       
    81   case  "+" => PL
       
    82   case  "*" => TI
       
    83   case  _ => T(s.toInt) 
       
    84 }
       
    85 
       
    86 comp("1 2 + 4 * 5 + 3 +".split(" ").toList.map(proc), Nil)
       
    87 
       
    88 
       
    89 
       
    90 
       
    91 // Sudoku 
       
    92 //========
       
    93 
       
    94 // THE POINT OF THIS CODE IS NOT TO BE SUPER
       
    95 // EFFICIENT AND FAST, just explaining exhaustive
       
    96 // depth-first search
       
    97 
       
    98 
       
    99 val game0 = """.14.6.3..
       
   100               |62...4..9
       
   101               |.8..5.6..
       
   102               |.6.2....3
       
   103               |.7..1..5.
       
   104               |5....9.6.
       
   105               |..6.2..3.
       
   106               |1..5...92
       
   107               |..7.9.41.""".stripMargin.replaceAll("\\n", "")
       
   108 
       
   109 type Pos = (Int, Int)
       
   110 val EmptyValue = '.'
       
   111 val MaxValue = 9
       
   112 
       
   113 val allValues = "123456789".toList
       
   114 val indexes = (0 to 8).toList
       
   115 
       
   116 
       
   117 def empty(game: String) = game.indexOf(EmptyValue)
       
   118 def isDone(game: String) = empty(game) == -1 
       
   119 def emptyPosition(game: String) = 
       
   120   (empty(game) % MaxValue, empty(game) / MaxValue)
       
   121 
       
   122 
       
   123 def get_row(game: String, y: Int) = 
       
   124   indexes.map(col => game(y * MaxValue + col))
       
   125 def get_col(game: String, x: Int) = 
       
   126   indexes.map(row => game(x + row * MaxValue))
       
   127 
       
   128 def get_box(game: String, pos: Pos): List[Char] = {
       
   129     def base(p: Int): Int = (p / 3) * 3
       
   130     val x0 = base(pos._1)
       
   131     val y0 = base(pos._2)
       
   132     val ys = (y0 until y0 + 3).toList
       
   133     (x0 until x0 + 3).toList.flatMap(x => ys.map(y => game(x + y * MaxValue)))
       
   134 }
       
   135 
       
   136 //get_row(game0, 0)
       
   137 //get_row(game0, 1)
       
   138 //get_col(game0, 0)
       
   139 //get_box(game0, (3, 1))
       
   140 
       
   141 
       
   142 // this is not mutable!!
       
   143 def update(game: String, pos: Int, value: Char): String = 
       
   144   game.updated(pos, value)
       
   145 
       
   146 def toAvoid(game: String, pos: Pos): List[Char] = 
       
   147   (get_col(game, pos._1) ++ get_row(game, pos._2) ++ get_box(game, pos))
       
   148 
       
   149 def candidates(game: String, pos: Pos): List[Char] = 
       
   150   allValues.diff(toAvoid(game, pos))
       
   151 
       
   152 //candidates(game0, (0,0))
       
   153 
       
   154 def pretty(game: String): String = 
       
   155   "\n" + (game.sliding(MaxValue, MaxValue).mkString("\n"))
       
   156 
       
   157 
       
   158 def search(game: String): List[String] = {
       
   159   if (isDone(game)) List(game)
       
   160   else {
       
   161     val cs = candidates(game, emptyPosition(game))
       
   162     cs.map(c => search(update(game, empty(game), c))).toList.flatten
       
   163   }
       
   164 }
       
   165 
       
   166 search(game0).map(pretty)
       
   167 
       
   168 val game1 = """23.915...
       
   169               |...2..54.
       
   170               |6.7......
       
   171               |..1.....9
       
   172               |89.5.3.17
       
   173               |5.....6..
       
   174               |......9.5
       
   175               |.16..7...
       
   176               |...329..1""".stripMargin.replaceAll("\\n", "")
       
   177 
       
   178 search(game1).map(pretty)
       
   179 
       
   180 // a game that is in the hard category
       
   181 val game2 = """8........
       
   182               |..36.....
       
   183               |.7..9.2..
       
   184               |.5...7...
       
   185               |....457..
       
   186               |...1...3.
       
   187               |..1....68
       
   188               |..85...1.
       
   189               |.9....4..""".stripMargin.replaceAll("\\n", "")
       
   190 
       
   191 search(game2).map(pretty)
       
   192 
       
   193 // game with multiple solutions
       
   194 val game3 = """.8...9743
       
   195               |.5...8.1.
       
   196               |.1.......
       
   197               |8....5...
       
   198               |...8.4...
       
   199               |...3....6
       
   200               |.......7.
       
   201               |.3.5...8.
       
   202               |9724...5.""".stripMargin.replaceAll("\\n", "")
       
   203 
       
   204 search(game3).map(pretty).foreach(println)
       
   205 
       
   206 // for measuring time
       
   207 def time_needed[T](i: Int, code: => T) = {
       
   208   val start = System.nanoTime()
       
   209   for (j <- 1 to i) code
       
   210   val end = System.nanoTime()
       
   211   s"${(end - start) / 1.0e9} secs"
       
   212 }
       
   213 
       
   214 time_needed(1, search(game2))
       
   215 
       
   216 
       
   217 
       
   218 // Tail recursion
       
   219 //================
       
   220 
       
   221 
       
   222 def fact(n: Long): Long = 
       
   223   if (n == 0) 1 else n * fact(n - 1)
       
   224 
       
   225 
       
   226 fact(10)              // ok
       
   227 fact(1000)            // silly
       
   228 fact(10000)           // produces a stackoverflow
       
   229 
       
   230 def factB(n: BigInt): BigInt = 
       
   231   if (n == 0) 1 else n * factB(n - 1)
       
   232 
       
   233 factB(1000)
       
   234 
       
   235 
       
   236 def factT(n: BigInt, acc: BigInt): BigInt =
       
   237   if (n == 0) acc else factT(n - 1, n * acc)
       
   238 
       
   239 factT(10, 1)
       
   240 println(factT(100000, 1))
       
   241 
       
   242 // there is a flag for ensuring a function is tail recursive
       
   243 import scala.annotation.tailrec
       
   244 
       
   245 @tailrec
       
   246 def factT(n: BigInt, acc: BigInt): BigInt =
       
   247   if (n == 0) acc else factT(n - 1, n * acc)
       
   248 
       
   249 factT(100000, 1)
       
   250 
       
   251 // for tail-recursive functions the Scala compiler
       
   252 // generates loop-like code, which does not need
       
   253 // to allocate stack-space in each recursive
       
   254 // call; Scala can do this only for tail-recursive
       
   255 // functions
       
   256 
       
   257 // tail recursive version that searches 
       
   258 // for all Sudoku solutions
       
   259 
       
   260 
       
   261 def searchT(games: List[String], sols: List[String]): List[String] = games match {
       
   262   case Nil => sols
       
   263   case game::rest => {
       
   264     if (isDone(game)) searchT(rest, game::sols)
       
   265     else {
       
   266       val cs = candidates(game, emptyPosition(game))
       
   267       searchT(cs.map(c => update(game, empty(game), c)) ::: rest, sols)
       
   268     }
       
   269   }
       
   270 }
       
   271 
       
   272 searchT(List(game3), List()).map(pretty)
       
   273 
       
   274 
       
   275 // tail recursive version that searches 
       
   276 // for a single solution
       
   277 
       
   278 def search1T(games: List[String]): Option[String] = games match {
       
   279   case Nil => None
       
   280   case game::rest => {
       
   281     if (isDone(game)) Some(game)
       
   282     else {
       
   283       val cs = candidates(game, emptyPosition(game))
       
   284       search1T(cs.map(c => update(game, empty(game), c)) ::: rest)
       
   285     }
       
   286   }
       
   287 }
       
   288 
       
   289 search1T(List(game3)).map(pretty)
       
   290 time_needed(1, search1T(List(game3)))
       
   291 time_needed(1, search1T(List(game2)))
       
   292 
       
   293 // game with multiple solutions
       
   294 val game3 = """.8...9743
       
   295               |.5...8.1.
       
   296               |.1.......
       
   297               |8....5...
       
   298               |...8.4...
       
   299               |...3....6
       
   300               |.......7.
       
   301               |.3.5...8.
       
   302               |9724...5.""".stripMargin.replaceAll("\\n", "")
       
   303 
       
   304 searchT(List(game3), Nil).map(pretty)
       
   305 search1T(List(game3)).map(pretty)
       
   306 
       
   307 // Moral: Whenever a recursive function is resource-critical
       
   308 // (i.e. works with large recursion depth), then you need to
       
   309 // write it in tail-recursive fashion.
       
   310 // 
       
   311 // Unfortuantely, Scala because of current limitations in 
       
   312 // the JVM is not as clever as other functional languages. It can 
       
   313 // only optimise "self-tail calls". This excludes the cases of 
       
   314 // multiple functions making tail calls to each other. Well,
       
   315 // nothing is perfect. 
       
   316 
       
   317 
     3 
   318 
     4 
   319 
     5 // Polymorphic Types
   320 // Polymorphic Types
     6 //===================
   321 //===================
     7 
   322 
    35   case x::xs => f(x)::map(xs, f) 
   350   case x::xs => f(x)::map(xs, f) 
    36 }
   351 }
    37 
   352 
    38 map(List(1, 2, 3, 4), (x: Int) => x.toString)
   353 map(List(1, 2, 3, 4), (x: Int) => x.toString)
    39 
   354 
    40 
       
    41 // Remember?
       
    42 def first[A, B](xs: List[A], f: A => Option[B]) : Option[B] = ...
       
    43 
   355 
    44 
   356 
    45 // distinct / distinctBy
   357 // distinct / distinctBy
    46 
   358 
    47 val ls = List(1,2,3,3,2,4,3,2,1)
   359 val ls = List(1,2,3,3,2,4,3,2,1)
   102 
   414 
   103 var arr = Array[Int]()
   415 var arr = Array[Int]()
   104 arr(0) = "Hello World"
   416 arr(0) = "Hello World"
   105 
   417 
   106 
   418 
       
   419 
       
   420 
       
   421 // Cool Stuff in Scala
       
   422 //=====================
       
   423 
       
   424 
       
   425 // Implicits or How to Pimp your Library
       
   426 //======================================
       
   427 //
       
   428 // For example adding your own methods to Strings:
       
   429 // Imagine you want to increment strings, like
       
   430 //
       
   431 //     "HAL".increment
       
   432 //
       
   433 // you can avoid ugly fudges, like a MyString, by
       
   434 // using implicit conversions.
       
   435 
       
   436 
       
   437 implicit class MyString(s: String) {
       
   438   def increment = s.map(c => (c + 1).toChar) 
       
   439 }
       
   440 
       
   441 "HAL".increment
       
   442 
       
   443 
       
   444 // Abstract idea:
       
   445 // In that version implicit conversions were used to solve the 
       
   446 // late extension problem; namely, given a class C and a class T, 
       
   447 // how to have C extend T without touching or recompiling C. 
       
   448 // Conversions add a wrapper when a member of T is requested 
       
   449 // from an instance of C.
       
   450 
       
   451 //Another example (TimeUnit in 2.13?)
       
   452 
       
   453 import scala.concurrent.duration.{TimeUnit,SECONDS,MINUTES}
       
   454 
       
   455 case class Duration(time: Long, unit: TimeUnit) {
       
   456   def +(o: Duration) = 
       
   457     Duration(time + unit.convert(o.time, o.unit), unit)
       
   458 }
       
   459 
       
   460 implicit class Int2Duration(that: Int) {
       
   461   def seconds = new Duration(that, SECONDS)
       
   462   def minutes = new Duration(that, MINUTES)
       
   463 }
       
   464 
       
   465 5.seconds + 2.minutes   //Duration(125L, SECONDS )
       
   466 2.minutes + 60.seconds
       
   467 
       
   468 
       
   469 
       
   470 
       
   471 // Regular expressions - the power of DSLs in Scala
       
   472 //==================================================
       
   473 
       
   474 abstract class Rexp
       
   475 case object ZERO extends Rexp                     // nothing
       
   476 case object ONE extends Rexp                      // the empty string
       
   477 case class CHAR(c: Char) extends Rexp             // a character c
       
   478 case class ALT(r1: Rexp, r2: Rexp) extends Rexp   // alternative  r1 + r2
       
   479 case class SEQ(r1: Rexp, r2: Rexp) extends Rexp   // sequence     r1 . r2  
       
   480 case class STAR(r: Rexp) extends Rexp             // star         r*
       
   481 
       
   482 
       
   483 
       
   484 // writing (ab)* in the format above is 
       
   485 // tedious
       
   486 val r0 = STAR(SEQ(CHAR('a'), CHAR('b')))
       
   487 
       
   488 
       
   489 // some convenience for typing in regular expressions
       
   490 import scala.language.implicitConversions    
       
   491 import scala.language.reflectiveCalls 
       
   492 
       
   493 def charlist2rexp(s: List[Char]): Rexp = s match {
       
   494   case Nil => ONE
       
   495   case c::Nil => CHAR(c)
       
   496   case c::s => SEQ(CHAR(c), charlist2rexp(s))
       
   497 }
       
   498 implicit def string2rexp(s: String): Rexp = 
       
   499   charlist2rexp(s.toList)
       
   500 
       
   501 
       
   502 val r1 = STAR("ab")
       
   503 val r2 = STAR(ALT("ab", "baa baa black sheep"))
       
   504 val r3 = STAR(SEQ("ab", ALT("a", "b")))
       
   505 
       
   506 implicit def RexpOps (r: Rexp) = new {
       
   507   def | (s: Rexp) = ALT(r, s)
       
   508   def % = STAR(r)
       
   509   def ~ (s: Rexp) = SEQ(r, s)
       
   510 }
       
   511 
       
   512 implicit def stringOps (s: String) = new {
       
   513   def | (r: Rexp) = ALT(s, r)
       
   514   def | (r: String) = ALT(s, r)
       
   515   def % = STAR(s)
       
   516   def ~ (r: Rexp) = SEQ(s, r)
       
   517   def ~ (r: String) = SEQ(s, r)
       
   518 }
       
   519 
       
   520 //example regular expressions
       
   521 val digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
       
   522 val sign = "+" | "-" | ""
       
   523 val number = sign ~ digit ~ digit.% 
   107 
   524 
   108 
   525 
   109 //
   526 //
   110 // Object Oriented Programming in Scala
   527 // Object Oriented Programming in Scala
   111 //
   528 //
   401 
   818 
   402 
   819 
   403 
   820 
   404 
   821 
   405 
   822 
   406 // Cool Stuff in Scala
       
   407 //=====================
       
   408 
       
   409 
       
   410 // Implicits or How to Pimp my Library
       
   411 //=====================================
       
   412 //
       
   413 // For example adding your own methods to Strings:
       
   414 // Imagine you want to increment strings, like
       
   415 //
       
   416 //     "HAL".increment
       
   417 //
       
   418 // you can avoid ugly fudges, like a MyString, by
       
   419 // using implicit conversions.
       
   420 
       
   421 
       
   422 implicit class MyString(s: String) {
       
   423   def increment = for (c <- s) yield (c + 1).toChar 
       
   424 }
       
   425 
       
   426 "HAL".increment
       
   427 
       
   428 
       
   429 // Abstract idea:
       
   430 // In that version implicit conversions were used to solve the 
       
   431 // late extension problem; namely, given a class C and a class T, 
       
   432 // how to have C extend T without touching or recompiling C. 
       
   433 // Conversions add a wrapper when a member of T is requested 
       
   434 // from an instance of C.
       
   435 
       
   436 //Another example (TimeUnit in 2.13?)
       
   437 
       
   438 case class Duration(time: Long, unit: TimeUnit) {
       
   439   def +(o: Duration) = Duration(time + unit.convert(o.time, o.unit), unit)
       
   440 }
       
   441 
       
   442 implicit class Int2Duration(that: Int) {
       
   443   def seconds = new Duration(that, SECONDS)
       
   444   def minutes = new Duration(that, MINUTES)
       
   445 }
       
   446 
       
   447 5.seconds + 2.minutes //Duration(125L, SECONDS )
       
   448 
       
   449 
       
   450 
       
   451 // Regular expressions - the power of DSLs in Scala
       
   452 //==================================================
       
   453 
       
   454 abstract class Rexp
       
   455 case object ZERO extends Rexp                     // nothing
       
   456 case object ONE extends Rexp                      // the empty string
       
   457 case class CHAR(c: Char) extends Rexp             // a character c
       
   458 case class ALT(r1: Rexp, r2: Rexp) extends Rexp   // alternative  r1 + r2
       
   459 case class SEQ(r1: Rexp, r2: Rexp) extends Rexp   // sequence     r1 . r2  
       
   460 case class STAR(r: Rexp) extends Rexp             // star         r*
       
   461 
       
   462 
       
   463 
       
   464 // writing (ab)* in the format above is 
       
   465 // tedious
       
   466 val r0 = STAR(SEQ(CHAR('a'), CHAR('b')))
       
   467 
       
   468 
       
   469 // some convenience for typing in regular expressions
       
   470 import scala.language.implicitConversions    
       
   471 import scala.language.reflectiveCalls 
       
   472 
       
   473 def charlist2rexp(s: List[Char]): Rexp = s match {
       
   474   case Nil => ONE
       
   475   case c::Nil => CHAR(c)
       
   476   case c::s => SEQ(CHAR(c), charlist2rexp(s))
       
   477 }
       
   478 implicit def string2rexp(s: String): Rexp = 
       
   479   charlist2rexp(s.toList)
       
   480 
       
   481 
       
   482 val r1 = STAR("ab")
       
   483 val r2 = STAR(ALT("ab", "baa baa black sheep"))
       
   484 val r3 = STAR(SEQ("ab", ALT("a", "b")))
       
   485 
       
   486 implicit def RexpOps (r: Rexp) = new {
       
   487   def | (s: Rexp) = ALT(r, s)
       
   488   def % = STAR(r)
       
   489   def ~ (s: Rexp) = SEQ(r, s)
       
   490 }
       
   491 
       
   492 
       
   493 implicit def stringOps (s: String) = new {
       
   494   def | (r: Rexp) = ALT(s, r)
       
   495   def | (r: String) = ALT(s, r)
       
   496   def % = STAR(s)
       
   497   def ~ (r: Rexp) = SEQ(s, r)
       
   498   def ~ (r: String) = SEQ(s, r)
       
   499 }
       
   500 
       
   501 //example regular expressions
       
   502 val digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
       
   503 val sign = "+" | "-" | ""
       
   504 val number = sign ~ digit ~ digit.% 
       
   505 
       
   506 
       
   507 
   823 
   508 // Lazy Evaluation
   824 // Lazy Evaluation
   509 //=================
   825 //=================
   510 //
   826 //
   511 // Do not evaluate arguments just yet:
   827 // Do not evaluate arguments just yet:
   517   for (j <- 1 to i) code
   833   for (j <- 1 to i) code
   518   val end = System.nanoTime()
   834   val end = System.nanoTime()
   519   (end - start)/(i * 1.0e9)
   835   (end - start)/(i * 1.0e9)
   520 }
   836 }
   521 
   837 
       
   838 
       
   839 // Mind-Blowing Regular Expressions
       
   840 
   522 // same examples using the internal regexes
   841 // same examples using the internal regexes
   523 val evil = "(a*)*b"
   842 val evil = "(a*)*b"
       
   843 
       
   844 
       
   845 println("a" * 100)
   524 
   846 
   525 ("a" * 10 ++ "b").matches(evil)
   847 ("a" * 10 ++ "b").matches(evil)
   526 ("a" * 10).matches(evil)
   848 ("a" * 10).matches(evil)
   527 ("a" * 10000).matches(evil)
   849 ("a" * 10000).matches(evil)
   528 ("a" * 20000).matches(evil)
   850 ("a" * 20000).matches(evil)
   529 ("a" * 50000).matches(evil)
   851 ("a" * 50000).matches(evil)
   530 
   852 
   531 time_needed(1, ("a" * 50000).matches(evil))
   853 time_needed(1, ("a" * 10000).matches(evil))