// 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, considerdef square(x: Int) = x * xsquare(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 isdef 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 primesprimes.take(100).toListtime_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 numbersdef fibIter(a: BigInt, b: BigInt): LazyList[BigInt] = a #:: fibIter(b, a + b)fibIter(1, 1).take(10).forcefibIter(8, 13).take(10).forcefibIter(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 Rexpcase object ZERO extends Rexp // nothingcase object ONE extends Rexp // the empty stringcase class CHAR(c: Char) extends Rexp // a character ccase class ALT(r1: Rexp, r2: Rexp) extends Rexp // alternative r1 + r2case 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 expressionsimport 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 expressionsval 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 leveldef 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").sizeenuml(2, "a").sizeenuml(3, "a").size // out of heap spacedef 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).forceenum(LazyList(ZERO, ONE, CHAR('a'), CHAR('b'))).take(5_000_000).forcedef 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// =====================================abstract class Animal case class Bird(name: String) extends Animal { override def toString = name}case class Mammal(name: String) extends Animalcase class Reptile(name: String) extends AnimalMammal("Zebra")println(Mammal("Zebra"))println(Mammal("Zebra").toString)Bird("Sparrow")println(Bird("Sparrow"))println(Bird("Sparrow").toString)Bird("Sparrow").copy(name = "House Sparrow")def group(a : Animal) = a match { case Bird(_) => "It's a bird" case Mammal(_) => "It's a mammal"}// There is a very convenient short-hand notation// for constructors:class Fraction(x: Int, y: Int) { def numer = x def denom = y}val half = new Fraction(1, 2)half.numercase class Fraction(numer: Int, denom: Int)val half = Fraction(1, 2)half.numerhalf.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)import scala.language.postfixOpsList(5,4,3,2,1).sorted.reverse// this could have equally been written asval test = Complex(1, 2).+(Complex (3, 4))// this applies to all methods, but requiresimport scala.language.postfixOpsList(5, 2, 3, 4).sortedList(5, 2, 3, 4) sorted// ...to allow the notation n + m * iimport scala.language.implicitConversions val i = Complex(0, 1)implicit def double2complex(re: Double) = Complex(re, 0)val inum1 = -2.0 + -1.5 * ival 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 no concern to us.// another example about Fractionsimport scala.language.implicitConversionsimport scala.language.reflectiveCallscase class Fraction(numer: Int, denom: Int) { override def toString = numer.toString + "/" + denom.toString def +(other: Fraction) = Fraction(numer * other.denom + other.numer * denom, denom * other.denom) def *(other: Fraction) = Fraction(numer * other.numer, denom * other.denom) }implicit def Int2Fraction(x: Int) = Fraction(x, 1)val half = Fraction(1, 2)val third = Fraction (1, 3)half + thirdhalf * third1 + 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 Statecase object Q0 extends Statecase object Q1 extends Statecase object Q2 extends Statecase object Q3 extends Statecase object Q4 extends Stateval 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) // truedfa.accepts("bbabaab".toList) // truedfa.accepts("baba".toList) // falsedfa.accepts("abc".toList) // 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 examplesval 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) // falsenfa.accepts("aaaaa".toList) // falsenfa.accepts("aaaaab".toList) // truenfa.accepts("aaaaabbb".toList) // truenfa.accepts("aaaaabbbaaa".toList) // falsenfa.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(nfa).accepts("aa".toList) // falsesubset(nfa).accepts("aaaaa".toList) // falsesubset(nfa).accepts("aaaaab".toList) // truesubset(nfa).accepts("aaaaabbb".toList) // truesubset(nfa).accepts("aaaaabbbaaa".toList) // falsesubset(nfa).accepts("ac".toList) // falseimport scala.math.pow// The End ... Almost Christmas//===============================// I hope you had fun!// A function should do one thing, and only one thing.// Make your variables immutable, unless there's a good // reason not to. Usually there is not.// I did it once, but this is actually not a good reason:// generating new labels:var counter = -1def Fresh(x: String) = { counter += 1 x ++ "_" ++ counter.toString()}Fresh("x")Fresh("x")// I think you can be productive on Day 1, but the // language is deep.//// http://scalapuzzlers.com//// http://www.latkin.org/blog/2017/05/02/when-the-scala-compiler-doesnt-help/val two = 0.2val one = 0.1val eight = 0.8val six = 0.6two - one == oneeight - six == twoeight - six// problems about equality and type-errorsList(1, 2, 3).contains("your cup") // should not compile, but retruns falseList(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. // Puzzlersval month = 12val day = 24val (hour, min, sec) = (12, 0, 0)// use lowercase names for variable //==================val oneTwo = Seq(1, 2, 3).permutationsif (oneTwo.length > 0) { println("Permutations of 1,2 and 3:") oneTwo.foreach(println)}val threeFour = Seq(3, 4, 5).permutationsif (!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.0val numbers = List("1", "2").toSet + "3"