progs/lecture4.scala
changeset 325 ca9c1cf929fa
parent 320 cdfb2ce30a3d
child 326 e5453add7df6
--- a/progs/lecture4.scala	Fri Nov 22 17:01:55 2019 +0000
+++ b/progs/lecture4.scala	Tue Nov 26 01:22:36 2019 +0000
@@ -2,6 +2,321 @@
 //=================
 
 
+// expressions (essentially trees)
+
+abstract class Exp
+case class N(n: Int) extends Exp                  // for numbers
+case class Plus(e1: Exp, e2: Exp) extends Exp
+case class Times(e1: Exp, e2: Exp) extends Exp
+
+def string(e: Exp) : String = e match {
+  case N(n) => s"$n"
+  case Plus(e1, e2) => s"(${string(e1)} + ${string(e2)})" 
+  case Times(e1, e2) => s"(${string(e1)} * ${string(e2)})"
+}
+
+val e = Plus(N(9), Times(N(3), N(4)))
+println(string(e))
+
+def eval(e: Exp) : Int = e match {
+  case N(n) => n
+  case Plus(e1, e2) => eval(e1) + eval(e2) 
+  case Times(e1, e2) => eval(e1) * eval(e2) 
+}
+
+println(eval(e))
+
+// simplification rules:
+// e + 0, 0 + e => e 
+// e * 0, 0 * e => 0
+// e * 1, 1 * e => e
+
+def simp(e: Exp) : Exp = e match {
+  case N(n) => N(n)
+  case Plus(e1, e2) => (simp(e1), simp(e2)) match {
+    case (N(0), e2s) => e2s
+    case (e1s, N(0)) => e1s
+    case (e1s, e2s) => Plus(e1s, e2s)
+  }  
+  case Times(e1, e2) => (simp(e1), simp(e2)) match {
+    case (N(0), _) => N(0)
+    case (_, N(0)) => N(0)
+    case (N(1), e2s) => e2s
+    case (e1s, N(1)) => e1s
+    case (e1s, e2s) => Times(e1s, e2s)
+  }  
+}
+
+
+val e2 = Times(Plus(N(0), N(1)), Plus(N(0), N(9)))
+println(string(e2))
+println(string(simp(e2)))
+
+
+// Tokens and Reverse Polish Notation
+abstract class Token
+case class T(n: Int) extends Token
+case object PL extends Token
+case object TI extends Token
+
+// transfroming an Exp into a list of tokens
+def rp(e: Exp) : List[Token] = e match {
+  case N(n) => List(T(n))
+  case Plus(e1, e2) => rp(e1) ::: rp(e2) ::: List(PL) 
+  case Times(e1, e2) => rp(e1) ::: rp(e2) ::: List(TI) 
+}
+println(string(e2))
+println(rp(e2))
+
+def comp(ls: List[Token], st: List[Int]) : Int = (ls, st) match {
+  case (Nil, st) => st.head 
+  case (T(n)::rest, st) => comp(rest, n::st)
+  case (PL::rest, n1::n2::st) => comp(rest, n1 + n2::st)
+  case (TI::rest, n1::n2::st) => comp(rest, n1 * n2::st)
+}
+
+comp(rp(e), Nil)
+
+def proc(s: String) : Token = s match {
+  case  "+" => PL
+  case  "*" => TI
+  case  _ => T(s.toInt) 
+}
+
+comp("1 2 + 4 * 5 + 3 +".split(" ").toList.map(proc), Nil)
+
+
+
+
+// Sudoku 
+//========
+
+// THE POINT OF THIS CODE IS NOT TO BE SUPER
+// EFFICIENT AND FAST, just explaining exhaustive
+// depth-first search
+
+
+val game0 = """.14.6.3..
+              |62...4..9
+              |.8..5.6..
+              |.6.2....3
+              |.7..1..5.
+              |5....9.6.
+              |..6.2..3.
+              |1..5...92
+              |..7.9.41.""".stripMargin.replaceAll("\\n", "")
+
+type Pos = (Int, Int)
+val EmptyValue = '.'
+val MaxValue = 9
+
+val allValues = "123456789".toList
+val indexes = (0 to 8).toList
+
+
+def empty(game: String) = game.indexOf(EmptyValue)
+def isDone(game: String) = empty(game) == -1 
+def emptyPosition(game: String) = 
+  (empty(game) % MaxValue, empty(game) / MaxValue)
+
+
+def get_row(game: String, y: Int) = 
+  indexes.map(col => game(y * MaxValue + col))
+def get_col(game: String, x: Int) = 
+  indexes.map(row => game(x + row * MaxValue))
+
+def get_box(game: String, pos: Pos): List[Char] = {
+    def base(p: Int): Int = (p / 3) * 3
+    val x0 = base(pos._1)
+    val y0 = base(pos._2)
+    val ys = (y0 until y0 + 3).toList
+    (x0 until x0 + 3).toList.flatMap(x => ys.map(y => game(x + y * MaxValue)))
+}
+
+//get_row(game0, 0)
+//get_row(game0, 1)
+//get_col(game0, 0)
+//get_box(game0, (3, 1))
+
+
+// this is not mutable!!
+def update(game: String, pos: Int, value: Char): String = 
+  game.updated(pos, value)
+
+def toAvoid(game: String, pos: Pos): List[Char] = 
+  (get_col(game, pos._1) ++ get_row(game, pos._2) ++ get_box(game, pos))
+
+def candidates(game: String, pos: Pos): List[Char] = 
+  allValues.diff(toAvoid(game, pos))
+
+//candidates(game0, (0,0))
+
+def pretty(game: String): String = 
+  "\n" + (game.sliding(MaxValue, MaxValue).mkString("\n"))
+
+
+def search(game: String): List[String] = {
+  if (isDone(game)) List(game)
+  else {
+    val cs = candidates(game, emptyPosition(game))
+    cs.map(c => search(update(game, empty(game), c))).toList.flatten
+  }
+}
+
+search(game0).map(pretty)
+
+val game1 = """23.915...
+              |...2..54.
+              |6.7......
+              |..1.....9
+              |89.5.3.17
+              |5.....6..
+              |......9.5
+              |.16..7...
+              |...329..1""".stripMargin.replaceAll("\\n", "")
+
+search(game1).map(pretty)
+
+// a game that is in the hard category
+val game2 = """8........
+              |..36.....
+              |.7..9.2..
+              |.5...7...
+              |....457..
+              |...1...3.
+              |..1....68
+              |..85...1.
+              |.9....4..""".stripMargin.replaceAll("\\n", "")
+
+search(game2).map(pretty)
+
+// game with multiple solutions
+val game3 = """.8...9743
+              |.5...8.1.
+              |.1.......
+              |8....5...
+              |...8.4...
+              |...3....6
+              |.......7.
+              |.3.5...8.
+              |9724...5.""".stripMargin.replaceAll("\\n", "")
+
+search(game3).map(pretty).foreach(println)
+
+// for measuring time
+def time_needed[T](i: Int, code: => T) = {
+  val start = System.nanoTime()
+  for (j <- 1 to i) code
+  val end = System.nanoTime()
+  s"${(end - start) / 1.0e9} secs"
+}
+
+time_needed(1, search(game2))
+
+
+
+// Tail recursion
+//================
+
+
+def fact(n: Long): Long = 
+  if (n == 0) 1 else n * fact(n - 1)
+
+
+fact(10)              // ok
+fact(1000)            // silly
+fact(10000)           // produces a stackoverflow
+
+def factB(n: BigInt): BigInt = 
+  if (n == 0) 1 else n * factB(n - 1)
+
+factB(1000)
+
+
+def factT(n: BigInt, acc: BigInt): BigInt =
+  if (n == 0) acc else factT(n - 1, n * acc)
+
+factT(10, 1)
+println(factT(100000, 1))
+
+// there is a flag for ensuring a function is tail recursive
+import scala.annotation.tailrec
+
+@tailrec
+def factT(n: BigInt, acc: BigInt): BigInt =
+  if (n == 0) acc else factT(n - 1, n * acc)
+
+factT(100000, 1)
+
+// for tail-recursive functions the Scala compiler
+// generates loop-like code, which does not need
+// to allocate stack-space in each recursive
+// call; Scala can do this only for tail-recursive
+// functions
+
+// tail recursive version that searches 
+// for all Sudoku solutions
+
+
+def searchT(games: List[String], sols: List[String]): List[String] = games match {
+  case Nil => sols
+  case game::rest => {
+    if (isDone(game)) searchT(rest, game::sols)
+    else {
+      val cs = candidates(game, emptyPosition(game))
+      searchT(cs.map(c => update(game, empty(game), c)) ::: rest, sols)
+    }
+  }
+}
+
+searchT(List(game3), List()).map(pretty)
+
+
+// tail recursive version that searches 
+// for a single solution
+
+def search1T(games: List[String]): Option[String] = games match {
+  case Nil => None
+  case game::rest => {
+    if (isDone(game)) Some(game)
+    else {
+      val cs = candidates(game, emptyPosition(game))
+      search1T(cs.map(c => update(game, empty(game), c)) ::: rest)
+    }
+  }
+}
+
+search1T(List(game3)).map(pretty)
+time_needed(1, search1T(List(game3)))
+time_needed(1, search1T(List(game2)))
+
+// game with multiple solutions
+val game3 = """.8...9743
+              |.5...8.1.
+              |.1.......
+              |8....5...
+              |...8.4...
+              |...3....6
+              |.......7.
+              |.3.5...8.
+              |9724...5.""".stripMargin.replaceAll("\\n", "")
+
+searchT(List(game3), Nil).map(pretty)
+search1T(List(game3)).map(pretty)
+
+// Moral: Whenever a recursive function is resource-critical
+// (i.e. works with large recursion depth), then you need to
+// write it in tail-recursive fashion.
+// 
+// Unfortuantely, Scala because of current limitations in 
+// the JVM is not as clever as other functional languages. It can 
+// only optimise "self-tail calls". This excludes the cases of 
+// multiple functions making tail calls to each other. Well,
+// nothing is perfect. 
+
+
+
+
 // Polymorphic Types
 //===================
 
@@ -38,9 +353,6 @@
 map(List(1, 2, 3, 4), (x: Int) => x.toString)
 
 
-// Remember?
-def first[A, B](xs: List[A], f: A => Option[B]) : Option[B] = ...
-
 
 // distinct / distinctBy
 
@@ -106,6 +418,111 @@
 
 
 
+// Cool Stuff in Scala
+//=====================
+
+
+// Implicits or How to Pimp your Library
+//======================================
+//
+// For example adding your own methods to Strings:
+// Imagine you want to increment strings, like
+//
+//     "HAL".increment
+//
+// you can avoid ugly fudges, like a MyString, by
+// using implicit conversions.
+
+
+implicit class MyString(s: String) {
+  def increment = s.map(c => (c + 1).toChar) 
+}
+
+"HAL".increment
+
+
+// Abstract idea:
+// In that version implicit conversions were used to solve the 
+// late extension problem; namely, given a class C and a class T, 
+// how to have C extend T without touching or recompiling C. 
+// Conversions add a wrapper when a member of T is requested 
+// from an instance of C.
+
+//Another example (TimeUnit in 2.13?)
+
+import scala.concurrent.duration.{TimeUnit,SECONDS,MINUTES}
+
+case class Duration(time: Long, unit: TimeUnit) {
+  def +(o: Duration) = 
+    Duration(time + unit.convert(o.time, o.unit), unit)
+}
+
+implicit class Int2Duration(that: Int) {
+  def seconds = new Duration(that, SECONDS)
+  def minutes = new Duration(that, MINUTES)
+}
+
+5.seconds + 2.minutes   //Duration(125L, SECONDS )
+2.minutes + 60.seconds
+
+
+
+
+// Regular expressions - the power of DSLs in Scala
+//==================================================
+
+abstract class Rexp
+case object ZERO extends Rexp                     // nothing
+case object ONE extends Rexp                      // the empty string
+case class CHAR(c: Char) extends Rexp             // a character c
+case class ALT(r1: Rexp, r2: Rexp) extends Rexp   // alternative  r1 + r2
+case class SEQ(r1: Rexp, r2: Rexp) extends Rexp   // sequence     r1 . r2  
+case class STAR(r: Rexp) extends Rexp             // star         r*
+
+
+
+// writing (ab)* in the format above is 
+// tedious
+val r0 = STAR(SEQ(CHAR('a'), CHAR('b')))
+
+
+// some convenience for typing in regular expressions
+import scala.language.implicitConversions    
+import scala.language.reflectiveCalls 
+
+def charlist2rexp(s: List[Char]): Rexp = s match {
+  case Nil => ONE
+  case c::Nil => CHAR(c)
+  case c::s => SEQ(CHAR(c), charlist2rexp(s))
+}
+implicit def string2rexp(s: String): Rexp = 
+  charlist2rexp(s.toList)
+
+
+val r1 = STAR("ab")
+val r2 = STAR(ALT("ab", "baa baa black sheep"))
+val r3 = STAR(SEQ("ab", ALT("a", "b")))
+
+implicit def RexpOps (r: Rexp) = new {
+  def | (s: Rexp) = ALT(r, s)
+  def % = STAR(r)
+  def ~ (s: Rexp) = SEQ(r, s)
+}
+
+implicit def stringOps (s: String) = new {
+  def | (r: Rexp) = ALT(s, r)
+  def | (r: String) = ALT(s, r)
+  def % = STAR(s)
+  def ~ (r: Rexp) = SEQ(s, r)
+  def ~ (r: String) = SEQ(s, r)
+}
+
+//example regular expressions
+val digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
+val sign = "+" | "-" | ""
+val number = sign ~ digit ~ digit.% 
+
+
 //
 // Object Oriented Programming in Scala
 //
@@ -403,107 +820,6 @@
 
 
 
-// Cool Stuff in Scala
-//=====================
-
-
-// Implicits or How to Pimp my Library
-//=====================================
-//
-// For example adding your own methods to Strings:
-// Imagine you want to increment strings, like
-//
-//     "HAL".increment
-//
-// you can avoid ugly fudges, like a MyString, by
-// using implicit conversions.
-
-
-implicit class MyString(s: String) {
-  def increment = for (c <- s) yield (c + 1).toChar 
-}
-
-"HAL".increment
-
-
-// Abstract idea:
-// In that version implicit conversions were used to solve the 
-// late extension problem; namely, given a class C and a class T, 
-// how to have C extend T without touching or recompiling C. 
-// Conversions add a wrapper when a member of T is requested 
-// from an instance of C.
-
-//Another example (TimeUnit in 2.13?)
-
-case class Duration(time: Long, unit: TimeUnit) {
-  def +(o: Duration) = Duration(time + unit.convert(o.time, o.unit), unit)
-}
-
-implicit class Int2Duration(that: Int) {
-  def seconds = new Duration(that, SECONDS)
-  def minutes = new Duration(that, MINUTES)
-}
-
-5.seconds + 2.minutes //Duration(125L, SECONDS )
-
-
-
-// Regular expressions - the power of DSLs in Scala
-//==================================================
-
-abstract class Rexp
-case object ZERO extends Rexp                     // nothing
-case object ONE extends Rexp                      // the empty string
-case class CHAR(c: Char) extends Rexp             // a character c
-case class ALT(r1: Rexp, r2: Rexp) extends Rexp   // alternative  r1 + r2
-case class SEQ(r1: Rexp, r2: Rexp) extends Rexp   // sequence     r1 . r2  
-case class STAR(r: Rexp) extends Rexp             // star         r*
-
-
-
-// writing (ab)* in the format above is 
-// tedious
-val r0 = STAR(SEQ(CHAR('a'), CHAR('b')))
-
-
-// some convenience for typing in regular expressions
-import scala.language.implicitConversions    
-import scala.language.reflectiveCalls 
-
-def charlist2rexp(s: List[Char]): Rexp = s match {
-  case Nil => ONE
-  case c::Nil => CHAR(c)
-  case c::s => SEQ(CHAR(c), charlist2rexp(s))
-}
-implicit def string2rexp(s: String): Rexp = 
-  charlist2rexp(s.toList)
-
-
-val r1 = STAR("ab")
-val r2 = STAR(ALT("ab", "baa baa black sheep"))
-val r3 = STAR(SEQ("ab", ALT("a", "b")))
-
-implicit def RexpOps (r: Rexp) = new {
-  def | (s: Rexp) = ALT(r, s)
-  def % = STAR(r)
-  def ~ (s: Rexp) = SEQ(r, s)
-}
-
-
-implicit def stringOps (s: String) = new {
-  def | (r: Rexp) = ALT(s, r)
-  def | (r: String) = ALT(s, r)
-  def % = STAR(s)
-  def ~ (r: Rexp) = SEQ(s, r)
-  def ~ (r: String) = SEQ(s, r)
-}
-
-//example regular expressions
-val digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
-val sign = "+" | "-" | ""
-val number = sign ~ digit ~ digit.% 
-
-
 
 // Lazy Evaluation
 //=================
@@ -519,13 +835,19 @@
   (end - start)/(i * 1.0e9)
 }
 
+
+// Mind-Blowing Regular Expressions
+
 // same examples using the internal regexes
 val evil = "(a*)*b"
 
+
+println("a" * 100)
+
 ("a" * 10 ++ "b").matches(evil)
 ("a" * 10).matches(evil)
 ("a" * 10000).matches(evil)
 ("a" * 20000).matches(evil)
 ("a" * 50000).matches(evil)
 
-time_needed(1, ("a" * 50000).matches(evil))
+time_needed(1, ("a" * 10000).matches(evil))