// Scala Lecture 4//=================// 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)// Remember?def first[A, B](xs: List[A], f: A => Option[B]) : Option[B] = ...// distinct / distinctByval ls = List(1,2,3,3,2,4,3,2,1)ls.distinctls.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 distinctdistinctBy(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 Scaladef id[T](x: T) : T = xval x = id(322) // Intval y = id("hey") // Stringval 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"//// Object Oriented Programming in Scala//// =====================================abstract class Animalcase class Bird(name: String) extends Animal { override def toString = name}case class Mammal(name: String) extends Animalcase class Reptile(name: String) extends AnimalBird("Sparrow")println(Bird("Sparrow"))println(Bird("Sparrow").toString)// you can override methodscase 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 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 not concern to us.// 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// another DFA with a Sink stateabstract class Scase object S0 extends Scase object S1 extends Scase object S2 extends Scase object Sink extends S// transition function with a sink stateval 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) // truedfa2.accepts("".toList) // falsedfa2.accepts("ab".toList) // false// we could also have a dfa for numbersval 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)) // truedfa3.accepts(Nil) // falsedfa3.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]() // 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(nfa1).accepts("aa".toList) // falsesubset(nfa1).accepts("aaaaa".toList) // falsesubset(nfa1).accepts("aaaaab".toList) // truesubset(nfa1).accepts("aaaaabbb".toList) // truesubset(nfa1).accepts("aaaaabbbaaa".toList) // falsesubset(nfa1).accepts("ac".toList) // false// Cool Stuff in Scala//=====================// Implicits or How to Pimp my 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 implicit conversions.implicit class MyString(s: String) { def increment = for (c <- s) yield (c + 1).toChar }"HAL".increment// 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*// writing (ab)* in the format above is // tediousval r0 = STAR(SEQ(CHAR('a'), CHAR('b')))// 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)val r1 = STAR("ab")val r2 = STAR(ALT("ab", "baa baa black sheep"))val r3 = STAR(SEQ("ab", ALT("a", "b")))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.% // Lazy Evaluation//=================//// Do not evaluate arguments just yet:// this uses the => in front of the type// of the code-argumentdef 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)}// same examples using the internal regexesval evil = "(a*)*b"("a" * 10 ++ "b").matches(evil)("a" * 10).matches(evil)("a" * 10000).matches(evil)("a" * 20000).matches(evil)("a" * 50000).matches(evil)time_needed(1, ("a" * 50000).matches(evil))