// Scala Lecture 3+ −
//=================+ −
+ −
+ −
// naive quicksort with "On" function+ −
+ −
def sortOn(f: Int => Int, xs: List[Int]) : List[Int] = {+ −
if (xs.size < 2) xs+ −
else {+ −
val pivot = xs.head+ −
val (left, right) = xs.partition(f(_) < f(pivot))+ −
sortOn(f, left) ::: pivot :: sortOn(f, right.tail)+ −
}+ −
} + −
+ −
sortOn(identity, List(99,99,99,98,10,-3,2)) + −
sortOn(n => - n, List(99,99,99,98,10,-3,2))+ −
+ −
+ −
// Recursion Again ;o)+ −
//====================+ −
+ −
+ −
// another well-known example: Towers of Hanoi+ −
//=============================================+ −
+ −
def move(from: Char, to: Char) =+ −
println(s"Move disc from $from to $to!")+ −
+ −
def hanoi(n: Int, from: Char, via: Char, to: Char) : Unit = {+ −
if (n == 0) ()+ −
else {+ −
hanoi(n - 1, from, to, via)+ −
move(from, to)+ −
hanoi(n - 1, via, from, to)+ −
}+ −
} + −
+ −
hanoi(4, 'A', 'B', 'C')+ −
+ −
+ −
+ −
// User-defined Datatypes+ −
//========================+ −
+ −
abstract class Tree+ −
case class Leaf(x: Int) extends Tree+ −
case class Node(s: String, left: Tree, right: Tree) extends Tree + −
+ −
val lf = Leaf(20)+ −
val tr = Node("foo", Leaf(10), Leaf(23))+ −
+ −
val lst : List[Tree] = List(lf, tr)+ −
+ −
+ −
abstract class Colour+ −
case object Red extends Colour + −
case object Green extends Colour + −
case object Blue extends Colour+ −
case object Yellow extends Colour+ −
+ −
+ −
def fav_colour(c: Colour) : Boolean = c match {+ −
case Green => true+ −
case _ => false + −
}+ −
+ −
fav_colour(Blue)+ −
+ −
+ −
// ... a tiny bit more useful: Roman Numerals+ −
+ −
sealed abstract class RomanDigit + −
case object I extends RomanDigit + −
case object V extends RomanDigit + −
case object X extends RomanDigit + −
case object L extends RomanDigit + −
case object C extends RomanDigit + −
case object D extends RomanDigit + −
case object M extends RomanDigit + −
+ −
type RomanNumeral = List[RomanDigit] + −
+ −
List(X,I,M,A)+ −
+ −
/*+ −
I -> 1+ −
II -> 2+ −
III -> 3+ −
IV -> 4+ −
V -> 5+ −
VI -> 6+ −
VII -> 7+ −
VIII -> 8+ −
IX -> 9+ −
X -> 10+ −
*/+ −
+ −
def RomanNumeral2Int(rs: RomanNumeral): Int = rs match { + −
case Nil => 0+ −
case M::r => 1000 + RomanNumeral2Int(r) + −
case C::M::r => 900 + RomanNumeral2Int(r)+ −
case D::r => 500 + RomanNumeral2Int(r)+ −
case C::D::r => 400 + RomanNumeral2Int(r)+ −
case C::r => 100 + RomanNumeral2Int(r)+ −
case X::C::r => 90 + RomanNumeral2Int(r)+ −
case L::r => 50 + RomanNumeral2Int(r)+ −
case X::L::r => 40 + RomanNumeral2Int(r)+ −
case X::r => 10 + RomanNumeral2Int(r)+ −
case I::X::r => 9 + RomanNumeral2Int(r)+ −
case V::r => 5 + RomanNumeral2Int(r)+ −
case I::V::r => 4 + RomanNumeral2Int(r)+ −
case I::r => 1 + RomanNumeral2Int(r)+ −
}+ −
+ −
RomanNumeral2Int(List(I,V)) // 4+ −
RomanNumeral2Int(List(I,I,I,I)) // 4 (invalid Roman number)+ −
RomanNumeral2Int(List(V,I)) // 6+ −
RomanNumeral2Int(List(I,X)) // 9+ −
RomanNumeral2Int(List(M,C,M,L,X,X,I,X)) // 1979+ −
RomanNumeral2Int(List(M,M,X,V,I,I)) // 2017+ −
+ −
+ −
// expressions (essentially trees)+ −
+ −
abstract class Exp+ −
case class N(n: Int) extends Exp // for numbers+ −
case class Plus(e1: Exp, e2: Exp) extends Exp+ −
case class Times(e1: Exp, e2: Exp) extends Exp+ −
+ −
def string(e: Exp) : String = e match {+ −
case N(n) => s"$n"+ −
case Plus(e1, e2) => s"(${string(e1)} + ${string(e2)})" + −
case Times(e1, e2) => s"(${string(e1)} * ${string(e2)})"+ −
}+ −
+ −
val e = Plus(N(9), Times(N(3), N(4)))+ −
e.toString+ −
println(string(e))+ −
+ −
def eval(e: Exp) : Int = e match {+ −
case N(n) => n+ −
case Plus(e1, e2) => eval(e1) + eval(e2) + −
case Times(e1, e2) => eval(e1) * eval(e2) + −
}+ −
+ −
println(eval(e))+ −
+ −
// simplification rules:+ −
// e + 0, 0 + e => e + −
// e * 0, 0 * e => 0+ −
// e * 1, 1 * e => e+ −
//+ −
// (....9 ....)+ −
+ −
def simp(e: Exp) : Exp = e match {+ −
case N(n) => N(n)+ −
case Plus(e1, e2) => (simp(e1), simp(e2)) match {+ −
case (N(0), e2s) => e2s+ −
case (e1s, N(0)) => e1s+ −
case (e1s, e2s) => Plus(e1s, e2s)+ −
} + −
case Times(e1, e2) => (simp(e1), simp(e2)) match {+ −
case (N(0), _) => N(0)+ −
case (_, N(0)) => N(0)+ −
case (N(1), e2s) => e2s+ −
case (e1s, N(1)) => e1s+ −
case (e1s, e2s) => Times(e1s, e2s)+ −
} + −
}+ −
+ −
+ −
val e2 = Times(Plus(N(0), N(1)), Plus(N(0), N(9)))+ −
println(string(e2))+ −
println(string(simp(e2)))+ −
+ −
+ −
+ −
// String interpolations as patterns+ −
+ −
val date = "2019-11-26"+ −
val s"$year-$month-$day" = date+ −
+ −
def parse_date(date: String) : Option[(Int, Int, Int)]= date match {+ −
case s"$year-$month-$day" => Some((day.toInt, month.toInt, year.toInt))+ −
case s"$day/$month/$year" => Some((day.toInt, month.toInt, year.toInt))+ −
case s"$day.$month.$year" => Some((day.toInt, month.toInt, year.toInt))+ −
case _ => None+ −
} + −
+ −
parse_date("2019-11-26")+ −
parse_date("26/11/2019")+ −
parse_date("26.11.2019")+ −
+ −
+ −
// guards in pattern-matching+ −
+ −
def foo(xs: List[Int]) : String = xs match {+ −
case Nil => s"this list is empty"+ −
case x :: xs if x % 2 == 0+ −
=> s"the first elemnt is even"+ −
case x :: y :: rest if x == y+ −
=> s"this has two elemnts that are the same"+ −
case hd :: tl => s"this list is standard $hd::$tl"+ −
}+ −
+ −
foo(Nil)+ −
foo(List(1,2,3))+ −
foo(List(1,2))+ −
foo(List(1,1,2,3))+ −
foo(List(2,2,2,3))+ −
+ −
// Tail recursion+ −
//================+ −
+ −
def fact(n: BigInt): BigInt = + −
if (n == 0) 1 else n * fact(n - 1)+ −
+ −
fact(10) //ok+ −
fact(10000) // produces a stackoverflow+ −
+ −
+ −
def factT(n: BigInt, acc: BigInt): BigInt =+ −
if (n == 0) acc else factT(n - 1, n * acc)+ −
+ −
factT(10, 1)+ −
println(factT(100000, 1))+ −
+ −
// there is a flag for ensuring a function is tail recursive+ −
import scala.annotation.tailrec+ −
+ −
@tailrec+ −
def factT(n: BigInt, acc: BigInt): BigInt =+ −
if (n == 0) acc else factT(n - 1, n * acc)+ −
+ −
+ −
+ −
// for tail-recursive functions the Scala compiler+ −
// generates loop-like code, which does not need+ −
// to allocate stack-space in each recursive+ −
// call; Scala can do this only for tail-recursive+ −
// functions+ −
+ −
def length(xs: List[Int]) : Int = xs match {+ −
case Nil => 0+ −
case _ :: tail => 1 + length(tail)+ −
}+ −
+ −
@tailrec+ −
def lengthT(xs: List[Int], acc : Int) : Int = xs match {+ −
case Nil => acc+ −
case _ :: tail => lengthT(tail, 1 + acc)+ −
}+ −
+ −
lengthT(List.fill(10000000)(1), 0)+ −
+ −
+ −
// Sudoku+ −
//========+ −
+ −
+ −
type Pos = (Int, Int)+ −
val emptyValue = '.'+ −
val maxValue = 9+ −
+ −
val allValues = "123456789".toList+ −
val indexes = (0 to 8).toList+ −
+ −
+ −
def empty(game: String) = game.indexOf(emptyValue)+ −
def isDone(game: String) = empty(game) == -1 + −
def emptyPosition(game: String) : Pos = + −
(empty(game) % maxValue, empty(game) / maxValue)+ −
+ −
+ −
def get_row(game: String, y: Int) = indexes.map(col => game(y * maxValue + col))+ −
def get_col(game: String, x: Int) = indexes.map(row => game(x + row * maxValue))+ −
+ −
def get_box(game: String, pos: Pos): List[Char] = {+ −
def base(p: Int): Int = (p / 3) * 3+ −
val x0 = base(pos._1)+ −
val y0 = base(pos._2)+ −
for (x <- (x0 until x0 + 3).toList;+ −
y <- (y0 until y0 + 3).toList) yield game(x + y * maxValue)+ −
} + −
+ −
+ −
def update(game: String, pos: Int, value: Char): String = + −
game.updated(pos, value)+ −
+ −
def toAvoid(game: String, pos: Pos): List[Char] = + −
(get_col(game, pos._1) ++ get_row(game, pos._2) ++ get_box(game, pos))+ −
+ −
def candidates(game: String, pos: Pos): List[Char] = + −
allValues.diff(toAvoid(game, pos))+ −
+ −
def search(game: String): List[String] = {+ −
if (isDone(game)) List(game)+ −
else + −
candidates(game, emptyPosition(game)).par.+ −
map(c => search(update(game, empty(game), c))).toList.flatten+ −
}+ −
+ −
+ −
def search1T(games: List[String]): Option[String] = games match {+ −
case Nil => None+ −
case game::rest => {+ −
if (isDone(game)) Some(game)+ −
else {+ −
val cs = candidates(game, emptyPosition(game))+ −
search1T(cs.map(c => update(game, empty(game), c)) ::: rest)+ −
}+ −
}+ −
}+ −
+ −
def pretty(game: String): String = + −
"\n" + (game.sliding(maxValue, maxValue).mkString(",\n"))+ −
+ −
+ −
// tail recursive version that searches + −
// for all solutions+ −
+ −
def searchT(games: List[String], sols: List[String]): List[String] = games match {+ −
case Nil => sols+ −
case game::rest => {+ −
if (isDone(game)) searchT(rest, game::sols)+ −
else {+ −
val cs = candidates(game, emptyPosition(game))+ −
searchT(cs.map(c => update(game, empty(game), c)) ::: rest, sols)+ −
}+ −
}+ −
}+ −
+ −
searchT(List(game3), List()).map(pretty)+ −
+ −
+ −
// tail recursive version that searches + −
// for a single solution+ −
+ −
def search1T(games: List[String]): Option[String] = games match {+ −
case Nil => None+ −
case game::rest => {+ −
if (isDone(game)) Some(game)+ −
else {+ −
val cs = candidates(game, emptyPosition(game))+ −
search1T(cs.map(c => update(game, empty(game), c)) ::: rest)+ −
}+ −
}+ −
}+ −
+ −
search1T(List(game3)).map(pretty)+ −
time_needed(10, search1T(List(game3)))+ −
+ −
+ −
// game with multiple solutions+ −
val game3 = """.8...9743+ −
|.5...8.1.+ −
|.1.......+ −
|8....5...+ −
|...8.4...+ −
|...3....6+ −
|.......7.+ −
|.3.5...8.+ −
|9724...5.""".stripMargin.replaceAll("\\n", "")+ −
+ −
searchT(List(game3), Nil).map(pretty)+ −
search1T(List(game3)).map(pretty)+ −
+ −
// Moral: Whenever a recursive function is resource-critical+ −
// (i.e. works with large recursion depth), then you need to+ −
// write it in tail-recursive fashion.+ −
// + −
// Unfortuantely, Scala because of current limitations in + −
// the JVM is not as clever as other functional languages. It can + −
// only optimise "self-tail calls". This excludes the cases of + −
// multiple functions making tail calls to each other. Well,+ −
// nothing is perfect. + −
+ −