// Scala Lecture 5//=================for (n <- (1 to 10).toList) yield { val add = 10 n + add}println(add)List(1,2,3,4).sum// extension methods// implicit conversions// (Immutable) OOP// Cool Stuff in Scala//=====================// Extensions 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 extensions.extension (s: String) { def increment = s.map(c => (c + 1).toChar)}"HAL".increment// a more relevant exampleimport 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)}extension (that: Int) { def seconds = Duration(that, SECONDS) def minutes = Duration(that, MINUTES)}2.minutes + 60.seconds5.seconds + 2.minutes //Duration(125, SECONDS )// Implicit Conversions//=====================// Regular expressions - the power of DSLs in Scala//==================================================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*val r = STAR(CHAR('a'))// 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))}given Conversion[String, Rexp] = (s => charlist2rexp(s.toList))extension (r: Rexp) { def | (s: Rexp) = ALT(r, s) def % = STAR(r) def ~ (s: Rexp) = SEQ(r, s)}val r1 = CHAR('a') | CHAR('b') | CHAR('c')val r2 = CHAR('a') ~ CHAR('b')val r3 : Rexp = "hello world"//example regular expressionsval 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 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 = Fraction(1, 2)half.numer// does not work with "vanilla" classeshalf match { case Fraction(x, y) => x / y}case class Fraction(numer: Int, denom: Int)val half = Fraction(1, 2)half.numerhalf.denom// works with case classeshalf match { case Fraction(x, y) => x / y}// 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)}// usual way to reference methods//object.method(....)val test = Complex(1, 2) + (Complex (3, 4))import scala.language.postfixOps(List(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)given Conversion[Double, Complex] = (re => 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) }given Conversion[Int, Fraction] = (x => 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) // false// 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".// In contrast 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))}given Conversion[String, Rexp] = (s => charlist2rexp(s.toList))extension (r: Rexp) { def | (s: Rexp) = ALT(r, s) def % = STAR(r) def ~ (s: Rexp) = SEQ(r, s)}//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 enuml(4, "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).force // out of memorydef 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))// 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