// 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+ −
// =====================================+ −
+ −
+ −
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+ −
+ −
Mammal("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.numer+ −
+ −
case class Fraction(numer: Int, denom: Int)+ −
+ −
val half = Fraction(1, 2)+ −
+ −
half.numer+ −
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)+ −
+ −
import scala.language.postfixOps+ −
List(5,4,3,2,1).sorted.reverse+ −
+ −
// 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 no 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.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 + third+ −
half * third+ −
+ −
1 + 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+ −
+ −
+ −
// 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(nfa).accepts("aa".toList) // false+ −
subset(nfa).accepts("aaaaa".toList) // false+ −
subset(nfa).accepts("aaaaab".toList) // true+ −
subset(nfa).accepts("aaaaabbb".toList) // true+ −
subset(nfa).accepts("aaaaabbbaaa".toList) // false+ −
subset(nfa).accepts("ac".toList) // false+ −
+ −
import 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 = -1+ −
+ −
def 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.2+ −
val one = 0.1+ −
val eight = 0.8+ −
val six = 0.6+ −
+ −
two - one == one+ −
eight - six == two+ −
eight - six+ −
+ −
+ −
// problems about equality and type-errors+ −
+ −
List(1, 2, 3).contains("your cup") // should not compile, but retruns false+ −
+ −
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"+ −