--- 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))