// Scala Lecture 3//=================// Pattern Matching//==================// A powerful tool which is supposed to come to Java in a few years// time (https://www.youtube.com/watch?v=oGll155-vuQ)...Scala already// has it for many years. Other functional languages have it already for// decades. I think I would be really upset if a programming language // I have to use does not have pattern matching....its is just so // useful. ;o)// The general schema://// expression match {// case pattern1 => expression1// case pattern2 => expression2// ...// case patternN => expressionN// }// rememberval lst = List(None, Some(1), Some(2), None, Some(3)).flattendef my_flatten(xs: List[Option[Int]]): List[Int] = { if (xs == Nil) Nil else if (xs.head == None) my_flatten(xs.tail) else xs.head.get :: my_flatten(xs.tail)}val lst = List(None, Some(1), Some(2), None, Some(3))def my_flatten(lst: List[Option[Int]]): List[Int] = lst match { case Nil => Nil case None::xs => my_flatten(xs) case Some(n)::xs => n::my_flatten(xs)}my_flatten(lst)Nil == List()// another example including a catch-all patterndef get_me_a_string(n: Int): String = n match { case 0 => "zero" case 1 => "one" case 2 => "two" case _ => "many"}get_me_a_string(10)// you can also have cases combineddef season(month: String) = month match { case "March" | "April" | "May" => "It's spring" case "June" | "July" | "August" => "It's summer" case "September" | "October" | "November" => "It's autumn" case "December" | "January" | "February" => "It's winter"}println(season("November"))// What happens if no case matches?println(season("foobar"))// we can also match more complicated pattern//// let's look at the Collatz function on binary strings// adding two binary strings in a very, very lazy mannerdef badd(s1: String, s2: String) : String = (BigInt(s1, 2) + BigInt(s2, 2)).toString(2)"111".dropRight(1)"111".lastdef bcollatz(s: String) : Long = (s.dropRight(1), s.last) match { case ("", '1') => 1 // we reached 1 case (rest, '0') => 1 + bcollatz(rest) // even number => divide by two case (rest, '1') => 1 + bcollatz(badd(s + '1', s)) // odd number => s + '1' is 2 * s + 1 // add another s gives 3 * s + 1 } bcollatz(6.toBinaryString)bcollatz(837799.toBinaryString)bcollatz(100000000000000000L.toBinaryString)bcollatz(BigInt("1000000000000000000000000000000000000000000000000000000000000000000000000000").toString(2))// User-defined Datatypes//========================abstract class Colourcase object Red extends Colour case object Green extends Colour case object Blue extends Colourdef fav_colour(c: Colour) : Boolean = c match { case Red => false case Green => true case Blue => false }fav_colour(Green)// actually colors can be written with "object",// because they do not take any argumentsabstract class Daycase object Monday extends Day case object Tuesday extends Day case object Wednesday extends Daycase object Thursday extends Day case object Friday extends Day case object Saturday extends Daycase object Sunday extends Day // ... a bit more useful: Roman Numeralsabstract 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] 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)) // 4RomanNumeral2Int(List(I,I,I,I)) // 4 (invalid Roman number)RomanNumeral2Int(List(V,I)) // 6RomanNumeral2Int(List(I,X)) // 9RomanNumeral2Int(List(M,C,M,L,X,X,I,X)) // 1979RomanNumeral2Int(List(M,M,X,V,I,I)) // 2017// another example//=================// Once upon a time, in a complete fictional country there were Persons...abstract class Personcase object King extends Personcase class Peer(deg: String, terr: String, succ: Int) extends Personcase class Knight(name: String) extends Personcase class Peasant(name: String) extends Personcase object Clown extends Persondef title(p: Person): String = p match { case King => "His Majesty the King" case Peer(deg, terr, _) => s"The ${deg} of ${terr}" case Knight(name) => s"Sir ${name}" case Peasant(name) => name case Clown => "My name is Boris Johnson"}title(Clown)def superior(p1: Person, p2: Person): Boolean = (p1, p2) match { case (King, _) => true case (Peer(_,_,_), Knight(_)) => true case (Peer(_,_,_), Peasant(_)) => true case (Peer(_,_,_), Clown) => true case (Knight(_), Peasant(_)) => true case (Knight(_), Clown) => true case (Clown, Peasant(_)) => true case _ => false}val people = List(Knight("David"), Peer("Duke", "Norfolk", 84), Peasant("Christian"), King, Clown)println(people.sortWith(superior(_, _)).mkString(", "))// Tail recursion//================def fact(n: Long): Long = if (n == 0) 1 else n * fact(n - 1)fact(10) //okfact(10000) // produces a stackoverflowdef factT(n: BigInt, acc: BigInt): BigInt = if (n == 0) acc else factT(n - 1, n * acc)factT(10, 1)factT(100000, 1)// there is a flag for ensuring a function is tail recursiveimport scala.annotation.tailrec@tailrecdef 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// sudoku againval game0 = """.14.6.3.. |62...4..9 |.8..5.6.. |.6.2....3 |.7..1..5. |5....9.6. |..6.2..3. |1..5...92 |..7.9.41.""".stripMargin.replaceAll("\\n", "")type Pos = (Int, Int)val EmptyValue = '.'val MaxValue = 9val allValues = "123456789".toListval indexes = (0 to 8).toListdef empty(game: String) = game.indexOf(EmptyValue)def isDone(game: String) = empty(game) == -1 def emptyPosition(game: String) = (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) val ys = (y0 until y0 + 3).toList (x0 until x0 + 3).toList.flatMap(x => ys.map(y => game(x + y * MaxValue)))}// this is not mutable!!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))//candidates(game0, (0,0))def pretty(game: String): String = "\n" + (game sliding (MaxValue, MaxValue) mkString "\n")/////////////////////// not tail recursive def search(game: String): List[String] = { if (isDone(game)) List(game) else { val cs = candidates(game, emptyPosition(game)) cs.map(c => search(update(game, empty(game), c))).toList.flatten }}// tail recursive version that searches // for all solutionsdef 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 solutiondef 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)// game with multiple solutionsval 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. // Polymorphic Types//===================// You do not want to write functions like contains, first // 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(King, Knight("foo"), Clown))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_int_list(xs, f) }map_int_list(List(1, 2, 3, 4), square)// Remember?def first[A, B](xs: List[A], f: A => Option[B]): Option[B] = ...// Cool Stuff//============// Implicits //===========//// 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 o r2 case class STAR(r: Rexp) extends Rexp // star r*// (ab)*val 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"))val r3 = STAR(ALT("ab", "baa baa black sheep"))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.% // The End//=========// A function should do one thing, and only one thing.// Make your variables immutable, unless there's a good // reason not to.// 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/List(1, 2, 3) contains "your mom"// I like best about Scala that it lets me often write// concise, readable code.// You can define your own while loopdef my_while(condition: => Boolean)(block: => Unit): Unit = if (condition) { block ; my_while(condition) { block } } else { }var x = 10my_while (x > 0) { println(s"$x") ; x = x - 1 }`symbol`symbol`