// Scala Lecture 2//=================// Option type//=============//in Java if something unusually happens, you return null;//in Scala you use Option// - if the value is present, you use Some(value)// - if no value is present, you use NoneList(7,2,3,4,5,6).find(_ < 4)List(5,6,7,8,9).find(_ < 4)// Values in types//// Boolean: // Int: // String: //// Option[String]:// val lst = List(None, Some(1), Some(2), None, Some(3))lst.flattenSome(1).getSome(1).isDefinedNone.isDefinedval ps = List((3, 0), (3, 2), (4, 2), (2, 0), (1, 0), (1, 1))for ((x, y) <- ps) yield { if (y == 0) None else Some(x / y)}// getOrElse is for setting a default valueval lst = List(None, Some(1), Some(2), None, Some(3))for (x <- lst) yield x.getOrElse(0)// error handling with Option (no exceptions)//// Try(something).getOrElse(what_to_do_in_an_exception)//import scala.util._import io.SourceSource.fromURL("""http://www.inf.kcl.ac.uk/staff/urbanc/""").mkStringTry(Source.fromURL("""http://www.inf.kcl.ac.uk/staff/urbanc/""").mkString).getOrElse("")Try(Some(Source.fromURL("""http://www.inf.kcl.ac.uk/staff/urbanc/""").mkString)).getOrElse(None)// a function that turns strings into numbersInteger.parseInt("12u34")def get_me_an_int(s: String): Option[Int] = Try(Some(Integer.parseInt(s))).getOrElse(None)val lst = List("12345", "foo", "5432", "bar", "x21")for (x <- lst) yield get_me_an_int(x)// summing all the numbersval sum = lst.flatMap(get_me_an_int(_)).sum// This may not look any better than working with null in Java, but to// see the value, you have to put yourself in the shoes of the// consumer of the get_me_an_int function, and imagine you didn't// write that function.//// In Java, if you didn't write this function, you'd have to depend on// the Javadoc of the get_me_an_int. If you didn't look at the Javadoc, // you might not know that get_me_an_int could return a null, and your // code could potentially throw a NullPointerException.// even Scala is not immune to problems like this:List(5,6,7,8,9).indexOf(7)// Type abbreviations//====================// some syntactic conveniencetype Pos = (int, Int)type Board = List[List[Int]]// 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 conversionsimplicit class MyString(s: String) { def increment = for (c <- s) yield (c + 1).toChar }"HAL".increment// No return in Scala//====================//You should not use "return" in Scala://// A return expression, when evaluated, abandons the // current computation and returns to the caller of the // function in which return appears."def sq1(x: Int): Int = x * xdef sq2(x: Int): Int = return x * xdef sumq(ls: List[Int]): Int = { (for (x <- ls) yield (return x * x)).sum[Int]}sumq(List(1,2,3,4))// last expression in a function is the return statementdef square(x: Int): Int = { println(s"The argument is ${x}.") x * x}// 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 ;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] = { ...}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)}// another exampledef get_me_a_string(n: Int): String = n match { case 0 => "zero" case 1 => "one" case 2 => "two" case _ => "many"}get_me_a_string(0)// 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"))// User-defined Datatypes//========================abstract class Treecase class Node(elem: Int, left: Tree, right: Tree) extends Treecase class Leaf() extends Treedef insert(tr: Tree, n: Int): Tree = tr match { case Leaf() => Node(n, Leaf(), Leaf()) case Node(m, left, right) => if (n == m) Node(m, left, right) else if (n < m) Node(m, insert(left, n), right) else Node(m, left, insert(right, n))}val t1 = Node(4, Node(2, Leaf(), Leaf()), Node(7, Leaf(), Leaf()))insert(t1, 3)def depth(tr: Tree): Int = tr match { case Leaf() => 0 case Node(_, left, right) => 1 + List(depth(left), depth(right)).max}def balance(tr: Tree): Int = tr match { case Leaf() => 0 case Node(_, left, right) => depth(left) - depth(right)}balance(insert(t1, 3))// another exampleabstract class Personcase class 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 class 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}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(", "))// Higher-Order Functions//========================// functions can take functions as argumentsval lst = (1 to 10).toListdef even(x: Int): Boolean = x % 2 == 0def odd(x: Int): Boolean = x % 2 == 1lst.filter(x => even(x))lst.filter(even(_))lst.filter(even)lst.find(_ > 8)def square(x: Int): Int = x * xlst.map(square)lst.map(square).filter(_ > 4)lst.map(square).filter(_ > 4).map(square)// in my collatz.scala//(1 to bnd).map(i => (collatz(i), i)).maxBy(_._1)// type of functions, for example f: Int => Intdef my_map_int(lst: List[Int], f: Int => Int): List[Int] = lst match { case Nil => Nil case x::xs => f(x)::my_map_int(xs, f)}my_map_int(lst, square)// other function types//// f1: (Int, Int) => Int// f2: List[String] => Option[Int]// ... def sumOf(f: Int => Int, lst: List[Int]): Int = lst match { case Nil => 0 case x::xs => f(x) + sumOf(f, xs)}def sum_squares(lst: List[Int]) = sumOf(square, lst)def sum_cubes(lst: List[Int]) = sumOf(x => x * x * x, lst)sum_squares(lst)sum_cubes(lst)// lets try it factorialdef fact(n: Int): Int = ...def sum_fact(lst: List[Int]) = sumOf(fact, lst)sum_fact(lst)// Avoid being mutable//=====================// a student showed me...import scala.collection.mutable.ListBufferdef collatz_max(bnd: Long): (Long, Long) = { val colNos = ListBuffer[(Long, Long)]() for (i <- (1L to bnd).toList) colNos += ((collatz(i), i)) colNos.max}def collatz_max(bnd: Long): (Long, Long) = { (1L to bnd).map((i) => (collatz(i), i)).maxBy(_._1)}//views -> lazy collectiondef collatz_max(bnd: Long): (Long, Long) = { (1L to bnd).view.map((i) => (collatz(i), i)).maxBy(_._1)}// raises a GC exception(1 to 1000000000).filter(_ % 2 == 0).take(10).toList// ==> java.lang.OutOfMemoryError: GC overhead limit exceeded(1 to 1000000000).view.filter(_ % 2 == 0).take(10).toList// Sudoku//========// THE POINT OF THIS CODE IS NOT TO BE SUPER// EFFICIENT AND FAST, just explaining exhaustive// depth-first searchval 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)))}//get_row(game0, 0)//get_row(game0, 1)//get_box(game0, (3,1))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")def search(game: String): List[String] = { if (isDone(game)) List(game) else candidates(game, emptyPosition(game)).map(c => search(update(game, empty(game), c))).toList.flatten}val game1 = """23.915... |...2..54. |6.7...... |..1.....9 |89.5.3.17 |5.....6.. |......9.5 |.16..7... |...329..1""".stripMargin.replaceAll("\\n", "")// game that is in the hard categoryval game2 = """8........ |..36..... |.7..9.2.. |.5...7... |....457.. |...1...3. |..1....68 |..85...1. |.9....4..""".stripMargin.replaceAll("\\n", "")// 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", "")search(game0).map(pretty)search(game1).map(pretty)// for measuring timedef 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) + " secs"}search(game2).map(pretty)search(game3).distinct.lengthtime_needed(3, search(game2))time_needed(3, search(game3))