diff -r 26058bf089ae -r 7d983ee99fcc progs/lecture4.scala --- a/progs/lecture4.scala Tue Nov 26 01:22:36 2019 +0000 +++ b/progs/lecture4.scala Tue Dec 03 01:22:16 2019 +0000 @@ -30,6 +30,8 @@ // e + 0, 0 + e => e // e * 0, 0 * e => 0 // e * 1, 1 * e => e +// +// (....0 ....) def simp(e: Exp) : Exp = e match { case N(n) => N(n) @@ -68,14 +70,14 @@ println(string(e2)) println(rp(e2)) -def comp(ls: List[Token], st: List[Int]) : Int = (ls, st) match { +def comp(ls: List[Token], st: List[Int] = Nil) : 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) +comp(rp(e)) def proc(s: String) : Token = s match { case "+" => PL @@ -106,6 +108,8 @@ |1..5...92 |..7.9.41.""".stripMargin.replaceAll("\\n", "") +candidates(game0, (0, 0)) + type Pos = (Int, Int) val EmptyValue = '.' val MaxValue = 9 @@ -125,6 +129,8 @@ def get_col(game: String, x: Int) = indexes.map(row => game(x + row * MaxValue)) +get_row(game0, 0) + def get_box(game: String, pos: Pos): List[Char] = { def base(p: Int): Int = (p / 3) * 3 val x0 = base(pos._1) @@ -154,7 +160,6 @@ def pretty(game: String): String = "\n" + (game.sliding(MaxValue, MaxValue).mkString("\n")) - def search(game: String): List[String] = { if (isDone(game)) List(game) else { @@ -163,6 +168,8 @@ } } +List(List("sol1"), List("sol2", "sol3")).flatten + search(game0).map(pretty) val game1 = """23.915... @@ -218,26 +225,33 @@ // Tail recursion //================ - -def fact(n: Long): Long = +@tailrec +def fact(n: BigInt): BigInt = if (n == 0) 1 else n * fact(n - 1) -fact(10) // ok -fact(1000) // silly -fact(10000) // produces a stackoverflow +fact(10) +fact(1000) +fact(100000) def factB(n: BigInt): BigInt = if (n == 0) 1 else n * factB(n - 1) +def factT(n: BigInt, acc: BigInt): BigInt = + if (n == 0) acc else factT(n - 1, n * acc) + + 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)) +println(factT(500000, 1)) + + + + // there is a flag for ensuring a function is tail recursive import scala.annotation.tailrec @@ -257,7 +271,7 @@ // tail recursive version that searches // for all Sudoku solutions - +@tailrec def searchT(games: List[String], sols: List[String]): List[String] = games match { case Nil => sols case game::rest => { @@ -305,7 +319,7 @@ search1T(List(game3)).map(pretty) // Moral: Whenever a recursive function is resource-critical -// (i.e. works with large recursion depth), then you need to +// (i.e. works with a large recursion depth), then you need to // write it in tail-recursive fashion. // // Unfortuantely, Scala because of current limitations in @@ -317,104 +331,6 @@ -// Polymorphic Types -//=================== - -// You do not want to write functions like contains, first, -// length and so on for every type of lists. - - -def length_string_list(lst: List[String]): Int = lst match { - case Nil => 0 - case x::xs => 1 + length_string_list(xs) -} - -def length_int_list(lst: List[Int]): Int = lst match { - case Nil => 0 - case x::xs => 1 + length_int_list(xs) -} - -length_string_list(List("1", "2", "3", "4")) -length_int_list(List(1, 2, 3, 4)) - -def length[A](lst: List[A]): Int = lst match { - case Nil => 0 - case x::xs => 1 + length(xs) -} -length(List("1", "2", "3", "4")) -length(List(1, 2, 3, 4)) - - -def map[A, B](lst: List[A], f: A => B): List[B] = lst match { - case Nil => Nil - case x::xs => f(x)::map(xs, f) -} - -map(List(1, 2, 3, 4), (x: Int) => x.toString) - - - -// distinct / distinctBy - -val ls = List(1,2,3,3,2,4,3,2,1) -ls.distinct - -ls.minBy(_._2) -ls.sortBy(_._1) - -def distinctBy[B, C](xs: List[B], - f: B => C, - acc: List[C] = Nil): List[B] = xs match { - case Nil => Nil - case x::xs => { - val res = f(x) - if (acc.contains(res)) distinctBy(xs, f, acc) - else x::distinctBy(xs, f, res::acc) - } -} - -// distinctBy with the identity function is -// just distinct -distinctBy(ls, (x: Int) => x) - - -val cs = List('A', 'b', 'a', 'c', 'B', 'D', 'd') - -distinctBy(cs, (c:Char) => c.toUpper) - - - -// Type inference is local in Scala - -def id[T](x: T) : T = x - -val x = id(322) // Int -val y = id("hey") // String -val z = id(Set[Int](1,2,3,4)) // Set[Int] - - - -// The type variable concept in Scala can get really complicated. -// -// - variance (OO) -// - bounds (subtyping) -// - quantification - -// Java has issues with this too: Java allows -// to write the following incorrect code, and -// only recovers by raising an exception -// at runtime. - -// Object[] arr = new Integer[10]; -// arr[0] = "Hello World"; - - -// Scala gives you a compile-time error, which -// is much better. - -var arr = Array[Int]() -arr(0) = "Hello World" - @@ -495,12 +411,14 @@ case c::Nil => CHAR(c) case c::s => SEQ(CHAR(c), charlist2rexp(s)) } + implicit def string2rexp(s: String): Rexp = charlist2rexp(s.toList) +"(a|b)" val r1 = STAR("ab") -val r2 = STAR(ALT("ab", "baa baa black sheep")) +val r2 = (STAR("ab")) | (STAR("ba")) val r3 = STAR(SEQ("ab", ALT("a", "b"))) implicit def RexpOps (r: Rexp) = new { @@ -518,323 +436,12 @@ } //example regular expressions -val digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" +val digit = ("0" | "1" | "2" | "3" | "4" | + "5" | "6" | "7" | "8" | "9") val sign = "+" | "-" | "" val number = sign ~ digit ~ digit.% -// -// Object Oriented Programming in Scala -// -// ===================================== - -abstract class Animal -case class Bird(name: String) extends Animal { - override def toString = name -} -case class Mammal(name: String) extends Animal -case class Reptile(name: String) extends Animal - -Bird("Sparrow") - -println(Bird("Sparrow")) -println(Bird("Sparrow").toString) - - -// you can override methods -case class Bird(name: String) extends Animal { - override def toString = name -} - - -// There is a very convenient short-hand notation -// for constructors: - -class Fraction(x: Int, y: Int) { - def numer = x - def denom = y -} - - -case class Fraction(numer: Int, denom: Int) - -val half = Fraction(1, 2) - -half.denom - - -// In mandelbrot.scala I used complex (imaginary) numbers -// and implemented the usual arithmetic operations for complex -// numbers. - -case class Complex(re: Double, im: Double) { - // represents the complex number re + im * i - def +(that: Complex) = Complex(this.re + that.re, this.im + that.im) - def -(that: Complex) = Complex(this.re - that.re, this.im - that.im) - def *(that: Complex) = Complex(this.re * that.re - this.im * that.im, - this.re * that.im + that.re * this.im) - def *(that: Double) = Complex(this.re * that, this.im * that) - def abs = Math.sqrt(this.re * this.re + this.im * this.im) -} - -val test = Complex(1, 2) + Complex (3, 4) - -// this could have equally been written as -val test = Complex(1, 2).+(Complex (3, 4)) - -// this applies to all methods, but requires -import scala.language.postfixOps - -List(5, 2, 3, 4).sorted -List(5, 2, 3, 4) sorted - - -// ...to allow the notation n + m * i -import scala.language.implicitConversions - -val i = Complex(0, 1) -implicit def double2complex(re: Double) = Complex(re, 0) - - -val inum1 = -2.0 + -1.5 * i -val inum2 = 1.0 + 1.5 * i - - - -// All is public by default....so no public is needed. -// You can have the usual restrictions about private -// values and methods, if you are MUTABLE !!! - -case class BankAccount(init: Int) { - - private var balance = init - - def deposit(amount: Int): Unit = { - if (amount > 0) balance = balance + amount - } - - def withdraw(amount: Int): Int = - if (0 < amount && amount <= balance) { - balance = balance - amount - balance - } else throw new Error("insufficient funds") -} - -// BUT since we are completely IMMUTABLE, this is -// virtually of not concern to us. - - - -// another example about Fractions -import scala.language.implicitConversions -import scala.language.reflectiveCalls - - -case class Fraction(numer: Int, denom: Int) { - override def toString = numer.toString + "/" + denom.toString - - def +(other: Fraction) = Fraction(numer + other.numer, denom + other.denom) - def /(other: Fraction) = Fraction(numer * other.denom, denom * other.numer) - def /% (other: Fraction) = Fraction(numer * other.denom, denom * other.numer) - -} - -implicit def Int2Fraction(x: Int) = Fraction(x, 1) - - -val half = Fraction(1, 2) -val third = Fraction (1, 3) - -half + third -half / third - -// not sure if one can get this to work -// properly, since Scala just cannot find out -// if / is for ints or for Fractions -(1 / 3) + half -(1 / 2) + third - -// either you have to force the Fraction-type by -// using a method that is not defined for ints -(1 /% 3) + half -(1 /% 2) + third - - -// ...or explicitly give the type in order to allow -// Scala to do the conversion to Fractions -((1:Fraction) / 3) + half -(1 / (3: Fraction)) + half - - - -// DFAs in Scala -//=============== -import scala.util.Try - - -// A is the state type -// C is the input (usually characters) - -case class DFA[A, C](start: A, // starting state - delta: (A, C) => A, // transition function - fins: A => Boolean) { // final states (Set) - - def deltas(q: A, s: List[C]) : A = s match { - case Nil => q - case c::cs => deltas(delta(q, c), cs) - } - - def accepts(s: List[C]) : Boolean = - Try(fins(deltas(start, s))) getOrElse false -} - -// the example shown in the handout -abstract class State -case object Q0 extends State -case object Q1 extends State -case object Q2 extends State -case object Q3 extends State -case object Q4 extends State - -val delta : (State, Char) => State = - { case (Q0, 'a') => Q1 - case (Q0, 'b') => Q2 - case (Q1, 'a') => Q4 - case (Q1, 'b') => Q2 - case (Q2, 'a') => Q3 - case (Q2, 'b') => Q2 - case (Q3, 'a') => Q4 - case (Q3, 'b') => Q0 - case (Q4, 'a') => Q4 - case (Q4, 'b') => Q4 - case _ => throw new Exception("Undefined") } - -val dfa = DFA(Q0, delta, Set[State](Q4)) - -dfa.accepts("abaaa".toList) // true -dfa.accepts("bbabaab".toList) // true -dfa.accepts("baba".toList) // false -dfa.accepts("abc".toList) // false - -// another DFA with a Sink state -abstract class S -case object S0 extends S -case object S1 extends S -case object S2 extends S -case object Sink extends S - -// transition function with a sink state -val sigma : (S, Char) => S = - { case (S0, 'a') => S1 - case (S1, 'a') => S2 - case _ => Sink - } - -val dfa2 = DFA(S0, sigma, Set[S](S2)) - -dfa2.accepts("aa".toList) // true -dfa2.accepts("".toList) // false -dfa2.accepts("ab".toList) // false - -// we could also have a dfa for numbers -val sigmai : (S, Int) => S = - { case (S0, 1) => S1 - case (S1, 1) => S2 - case _ => Sink - } - -val dfa3 = DFA(S0, sigmai, Set[S](S2)) - -dfa3.accepts(List(1, 1)) // true -dfa3.accepts(Nil) // false -dfa3.accepts(List(1, 2)) // false - - - - -// NFAs (Nondeterministic Finite Automata) - - -case class NFA[A, C](starts: Set[A], // starting states - delta: (A, C) => Set[A], // transition function - fins: A => Boolean) { // final states - - // given a state and a character, what is the set of - // next states? if there is none => empty set - def next(q: A, c: C) : Set[A] = - Try(delta(q, c)) getOrElse Set[A]() - - def nexts(qs: Set[A], c: C) : Set[A] = - qs.flatMap(next(_, c)) - - // depth-first version of accepts - def search(q: A, s: List[C]) : Boolean = s match { - case Nil => fins(q) - case c::cs => next(q, c).exists(search(_, cs)) - } - - def accepts(s: List[C]) : Boolean = - starts.exists(search(_, s)) -} - - - -// NFA examples - -val nfa_trans1 : (State, Char) => Set[State] = - { case (Q0, 'a') => Set(Q0, Q1) - case (Q0, 'b') => Set(Q2) - case (Q1, 'a') => Set(Q1) - case (Q2, 'b') => Set(Q2) } - -val nfa = NFA(Set[State](Q0), nfa_trans1, Set[State](Q2)) - -nfa.accepts("aa".toList) // false -nfa.accepts("aaaaa".toList) // false -nfa.accepts("aaaaab".toList) // true -nfa.accepts("aaaaabbb".toList) // true -nfa.accepts("aaaaabbbaaa".toList) // false -nfa.accepts("ac".toList) // false - - -// Q: Why the kerfuffle about the polymorphic types in DFAs/NFAs? -// A: Subset construction. Here the state type for the DFA is -// sets of states. - -def subset[A, C](nfa: NFA[A, C]) : DFA[Set[A], C] = { - DFA(nfa.starts, - { case (qs, c) => nfa.nexts(qs, c) }, - _.exists(nfa.fins)) -} - -subset(nfa1).accepts("aa".toList) // false -subset(nfa1).accepts("aaaaa".toList) // false -subset(nfa1).accepts("aaaaab".toList) // true -subset(nfa1).accepts("aaaaabbb".toList) // true -subset(nfa1).accepts("aaaaabbbaaa".toList) // false -subset(nfa1).accepts("ac".toList) // false - - - - - - - - -// Lazy Evaluation -//================= -// -// Do not evaluate arguments just yet: -// this uses the => in front of the type -// of the code-argument - -def time_needed[T](i: Int, code: => T) = { - val start = System.nanoTime() - for (j <- 1 to i) code - val end = System.nanoTime() - (end - start)/(i * 1.0e9) -} - // Mind-Blowing Regular Expressions @@ -844,10 +451,10 @@ println("a" * 100) -("a" * 10 ++ "b").matches(evil) +("a" * 10000).matches(evil) ("a" * 10).matches(evil) ("a" * 10000).matches(evil) ("a" * 20000).matches(evil) ("a" * 50000).matches(evil) -time_needed(1, ("a" * 10000).matches(evil)) +time_needed(1, ("a" * 50000).matches(evil))