diff -r 289b85843ffd -r 557d18cce0f0 progs/lecture5.scala --- a/progs/lecture5.scala Thu Dec 08 22:19:21 2022 +0000 +++ b/progs/lecture5.scala Sat Dec 17 12:42:49 2022 +0000 @@ -1,215 +1,6 @@ // Scala Lecture 5 //================= -// Questions at -// -// pollev.com/cfltutoratki576 - -(n, m) match { - case Pat1 => - case _ => -} - -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") } - - -class Foo(i: Int) - -val v = new Foo(10) - -case class Bar(i: Int) - -val v = Bar(10) - - - - - - - - - - - - -// Laziness with style -//===================== - -// The concept of lazy evaluation doesn’t really -// exist in non-functional languages. C-like languages -// are (sort of) strict. To see the difference, consider - -def square(x: Int) = x * x - -square(42 + 8) - -// This is called "strict evaluation". - -// On the contrary, say we have a pretty expensive operation: - -def peop(n: BigInt): Boolean = peop(n + 1) - -val a = "foo" -val b = "foo" - -if (a == b || peop(0)) println("true") else println("false") - -// This is called "lazy evaluation": -// you delay compuation until it is really -// needed. Once calculated though, the result -// does not need to be re-calculated. - -// A useful example is - -def time_needed[T](i: Int, code: => T) = { - val start = System.nanoTime() - for (j <- 1 to i) code - val end = System.nanoTime() - f"${(end - start) / (i * 1.0e9)}%.6f secs" -} - -// A slightly less obvious example: Prime Numbers. -// (I do not care how many) primes: 2, 3, 5, 7, 9, 11, 13 .... - -def generatePrimes (s: LazyList[Int]): LazyList[Int] = - s.head #:: generatePrimes(s.tail.filter(_ % s.head != 0)) - -val primes = generatePrimes(LazyList.from(2)) - -// the first 10 primes -primes.take(100).toList - -time_needed(1, primes.filter(_ > 100).take(3000).toList) -time_needed(1, primes.filter(_ > 100).take(3000).toList) - -// A Stream (LazyList) of successive numbers: - -LazyList.from(2).take(10) -LazyList.from(2).take(10).force - -// An Iterative version of the Fibonacci numbers -def fibIter(a: BigInt, b: BigInt): LazyList[BigInt] = - a #:: fibIter(b, a + b) - - -fibIter(1, 1).take(10).force -fibIter(8, 13).take(10).force - -fibIter(1, 1).drop(10000).take(1) -fibIter(1, 1).drop(10000).take(1).force - - -// LazyLists are good for testing - - -// Regular expressions - the power of DSLs in Scala -// and Laziness -//================================================== - -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* - - -// 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) - -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.% - -// Task: enumerate exhaustively regular expressions -// starting from small ones towards bigger ones. - -// 1st idea: enumerate them all in a Set -// up to a level - -def enuml(l: Int, s: String) : Set[Rexp] = l match { - case 0 => Set(ZERO, ONE) ++ s.map(CHAR).toSet - case n => - val rs = enuml(n - 1, s) - rs ++ - (for (r1 <- rs; r2 <- rs) yield ALT(r1, r2)) ++ - (for (r1 <- rs; r2 <- rs) yield SEQ(r1, r2)) ++ - (for (r1 <- rs) yield STAR(r1)) -} - -enuml(1, "a") -enuml(1, "a").size -enuml(2, "a").size -enuml(3, "a").size // out of heap space - - - -def enum(rs: LazyList[Rexp]) : LazyList[Rexp] = - rs #::: enum( (for (r1 <- rs; r2 <- rs) yield ALT(r1, r2)) #::: - (for (r1 <- rs; r2 <- rs) yield SEQ(r1, r2)) #::: - (for (r1 <- rs) yield STAR(r1)) ) - - -enum(LazyList(ZERO, ONE, CHAR('a'), CHAR('b'))).take(200).force -enum(LazyList(ZERO, ONE, CHAR('a'), CHAR('b'))).take(5_000_000).force - - -def depth(r: Rexp) : Int = r match { - case ZERO => 0 - case ONE => 0 - case CHAR(_) => 0 - case ALT(r1, r2) => Math.max(depth(r1), depth(r2)) + 1 - case SEQ(r1, r2) => Math.max(depth(r1), depth(r2)) + 1 - case STAR(r1) => depth(r1) + 1 -} - - -val is = - (enum(LazyList(ZERO, ONE, CHAR('a'), CHAR('b'))) - .dropWhile(depth(_) < 3) - .take(10).foreach(println)) - - - // (Immutable) // Object Oriented Programming in Scala // ===================================== @@ -264,7 +55,7 @@ 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 foo(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) @@ -272,10 +63,12 @@ def abs = Math.sqrt(this.re * this.re + this.im * this.im) } +object.method(....) + val test = Complex(1, 2) + Complex (3, 4) import scala.language.postfixOps -List(5,4,3,2,1).sorted.reverse +(List(5,4,3,2,1) sorted) reverse // this could have equally been written as val test = Complex(1, 2).+(Complex (3, 4)) @@ -465,6 +258,172 @@ import scala.math.pow +// Laziness with style +//===================== + +// The concept of lazy evaluation doesn’t really +// exist in non-functional languages. C-like languages +// are (sort of) strict. To see the difference, consider + +def square(x: Int) = x * x + +square(42 + 8) + +// This is called "strict evaluation". + +// On the contrary, say we have a pretty expensive operation: + +def peop(n: BigInt): Boolean = peop(n + 1) + +val a = "foo" +val b = "foo" + +if (a == b || peop(0)) println("true") else println("false") + +// This is called "lazy evaluation": +// you delay compuation until it is really +// needed. Once calculated though, the result +// does not need to be re-calculated. + +// A useful example is + +def time_needed[T](i: Int, code: => T) = { + val start = System.nanoTime() + for (j <- 1 to i) code + val end = System.nanoTime() + f"${(end - start) / (i * 1.0e9)}%.6f secs" +} + +// A slightly less obvious example: Prime Numbers. +// (I do not care how many) primes: 2, 3, 5, 7, 9, 11, 13 .... + +def generatePrimes (s: LazyList[Int]): LazyList[Int] = + s.head #:: generatePrimes(s.tail.filter(_ % s.head != 0)) + +val primes = generatePrimes(LazyList.from(2)) + +// the first 10 primes +primes.take(100).toList + +time_needed(1, primes.filter(_ > 100).take(3000).toList) +time_needed(1, primes.filter(_ > 100).take(3000).toList) + +// A Stream (LazyList) of successive numbers: + +LazyList.from(2).take(10) +LazyList.from(2).take(10).force + +// An Iterative version of the Fibonacci numbers +def fibIter(a: BigInt, b: BigInt): LazyList[BigInt] = + a #:: fibIter(b, a + b) + + +fibIter(1, 1).take(10).force +fibIter(8, 13).take(10).force + +fibIter(1, 1).drop(10000).take(1) +fibIter(1, 1).drop(10000).take(1).force + + +// LazyLists are good for testing + + +// Regular expressions - the power of DSLs in Scala +// and Laziness +//================================================== + +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* + + +// 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) + +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.% + +// Task: enumerate exhaustively regular expressions +// starting from small ones towards bigger ones. + +// 1st idea: enumerate them all in a Set +// up to a level + +def enuml(l: Int, s: String) : Set[Rexp] = l match { + case 0 => Set(ZERO, ONE) ++ s.map(CHAR).toSet + case n => + val rs = enuml(n - 1, s) + rs ++ + (for (r1 <- rs; r2 <- rs) yield ALT(r1, r2)) ++ + (for (r1 <- rs; r2 <- rs) yield SEQ(r1, r2)) ++ + (for (r1 <- rs) yield STAR(r1)) +} + +enuml(1, "a") +enuml(1, "a").size +enuml(2, "a").size +enuml(3, "a").size // out of heap space + + + +def enum(rs: LazyList[Rexp]) : LazyList[Rexp] = + rs #::: enum( (for (r1 <- rs; r2 <- rs) yield ALT(r1, r2)) #::: + (for (r1 <- rs; r2 <- rs) yield SEQ(r1, r2)) #::: + (for (r1 <- rs) yield STAR(r1)) ) + + +enum(LazyList(ZERO, ONE, CHAR('a'), CHAR('b'))).take(200).force +enum(LazyList(ZERO, ONE, CHAR('a'), CHAR('b'))).take(5_000_000).force + + +def depth(r: Rexp) : Int = r match { + case ZERO => 0 + case ONE => 0 + case CHAR(_) => 0 + case ALT(r1, r2) => Math.max(depth(r1), depth(r2)) + 1 + case SEQ(r1, r2) => Math.max(depth(r1), depth(r2)) + 1 + case STAR(r1) => depth(r1) + 1 +} + + +val is = + (enum(LazyList(ZERO, ONE, CHAR('a'), CHAR('b'))) + .dropWhile(depth(_) < 3) + .take(10).foreach(println)) + + @@ -525,59 +484,4 @@ List(1, 2, 3) == Vector(1, 2, 3) // again should not compile, but returns true -// I like best about Scala that it lets me often write -// concise, readable code. And it hooks up with the -// Isabelle theorem prover. - -// Puzzlers - -val month = 12 -val day = 24 -val (hour, min, sec) = (12, 0, 0) - -// use lowercase names for variable - - -//================== -val oneTwo = Seq(1, 2, 3).permutations - -if (oneTwo.length > 0) { - println("Permutations of 1,2 and 3:") - oneTwo.foreach(println) -} - -val threeFour = Seq(3, 4, 5).permutations - -if (!threeFour.isEmpty) { - println("Permutations of 3, 4 and 5:") - threeFour.foreach(println) -} - -//================== -val (a, b, c) = - if (4 < 5) { - "bar" - } else { - Some(10) - } - -//Because when an expression has multiple return branches, Scala tries to -//be helpful, by picking the first common ancestor type of all the -//branches as the type of the whole expression. -// -//In this case, one branch has type String and the other has type -//Option[Int], so the compiler decides that what the developer really -//wants is for the whole if/else expression to have type Serializable, -//since that’s the most specific type to claim both String and Option as -//descendants. -// -//And guess what, Tuple3[A, B, C] is also Serializable, so as far as the -//compiler is concerned, the assignment of the whole mess to (a, b, c) -//can’t be proven invalid. So it gets through with a warning, -//destined to fail at runtime. - - -//================ -// does not work anymore in 2.13.0 -val numbers = List("1", "2").toSet + "3"