progs/lecture4.scala
changeset 326 e5453add7df6
parent 325 ca9c1cf929fa
child 380 d19b0a50ceb9
--- 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))