diff -r 2969ee4a6cee -r ca9c1cf929fa progs/lecture4.scala --- 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))