diff -r 61d9a5ac6430 -r 72f7dd1a3754 progs/lecture2.scala --- a/progs/lecture2.scala Tue Nov 14 22:19:04 2017 +0000 +++ b/progs/lecture2.scala Fri Nov 17 02:13:40 2017 +0000 @@ -2,10 +2,82 @@ //================= +// Overloaded math operations + +(100 / 4) + +(100 / 3) + +(100.toDouble / 3.toDouble) + + +// For-Comprehensions again +//========================== + +def square(n: Int) : Int = n * n + +for (n <- (1 to 10).toList) yield { + val res = square(n) + res +} + +// like in functions, the "last" item inside the yield +// will be returned; the last item is not necessarily +// the last line + +for (n <- (1 to 10).toList) yield { + if (n % 2 == 0) n + else square(n) +} + + +// ...please, please do not write: +val lst = List(1, 2, 3, 4, 5, 6, 7, 8, 9) + +for (i <- (0 until lst.length).toList) yield square(lst(i)) + +// this is just so prone to off-by-one errors; +// write instead + +for (e <- lst) yield square(e) + + +//this works for sets as well +val st = Set(1, 2, 3, 4, 5, 6, 7, 8, 9) + +for (e <- st) yield { + if (e < 5) e else square(e) +} + + + +// Side-Effects +//============== + +// with only a side-effect (no list is produced), +// has no "yield" + +for (n <- (1 to 10)) println(n) + + +for (n <- (1 to 10)) { + print("The number is: ") + print(n) + print("\n") +} + + + + +// know when to use yield and when not: + +for (e <- Set(1, 2, 3, 4, 5, 6, 7, 8, 9); if e < 5) yield square(e) + + // Option type //============= -//in Java if something unusually happens, you return null; +//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 None @@ -15,15 +87,7 @@ List(5,6,7,8,9).find(_ < 4) -// Values in types -// -// Boolean: -// Int: -// String: -// -// Option[String]: -// - +// some operations on Option's val lst = List(None, Some(1), Some(2), None, Some(3)) @@ -40,38 +104,54 @@ if (y == 0) None else Some(x / y) } -// getOrElse is for setting a default value +// use .getOrElse is for setting a default value val lst = List(None, Some(1), Some(2), None, Some(3)) + for (x <- lst) yield x.getOrElse(0) -// error handling with Option (no exceptions) +// error handling with Options (no exceptions) +// +// Try(....) // // Try(something).getOrElse(what_to_do_in_an_exception) // import scala.util._ + +Try(1 + 3) +Try(9 / 0) + +Try(9 / 3).getOrElse(42) +Try(9 / 0).getOrElse(42) + + import io.Source -Source.fromURL("""http://www.inf.kcl.ac.uk/staff/urbanc/""").mkString +val my_url = """https://nms.kcl.ac.uk/christian.urban""" + +Source.fromURL(my_url).mkString -Try(Source.fromURL("""http://www.inf.kcl.ac.uk/staff/urbanc/""").mkString).getOrElse("") +Try(Source.fromURL(my_url).mkString).getOrElse("") -Try(Some(Source.fromURL("""http://www.inf.kcl.ac.uk/staff/urbanc/""").mkString)).getOrElse(None) +Try(Some(Source.fromURL(my_url).mkString)).getOrElse(None) + // a function that turns strings into numbers -Integer.parseInt("12u34") +Integer.parseInt("1234") + 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 numbers -val sum = lst.flatMap(get_me_an_int(_)).sum +val sum = (for (i <- lst) yield get_me_an_int(i)).flatten.sum // This may not look any better than working with null in Java, but to @@ -80,12 +160,11 @@ // 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, +// the Javadoc of 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) @@ -93,27 +172,166 @@ +// Higher-Order Functions +//======================== + +// functions can take functions as arguments + +val lst = (1 to 10).toList + +def even(x: Int) : Boolean = x % 2 == 0 +def odd(x: Int) : Boolean = x % 2 == 1 + +lst.filter(x => even(x)) +lst.filter(even(_)) +lst.filter(even) + +lst.find(_ > 8) + +// map applies a function to each element of a list + +def square(x: Int): Int = x * x + +lst.map(square) + +lst.map(square).filter(_ > 4) + +lst.map(square).filter(_ > 4).map(square) + +// map works for most collection types, including sets +Set(1, 3, 6).map(square) + + +// Why could functions as arguments be useful? +// +// Consider the sum between a and b: + +def sumInts(a: Int, b: Int) : Int = + if (a > b) 0 else a + sumInts(a + 1, b) + + +sumInt(10, 16) + +// sum squares +def square(n: Int) : Int = n * n + +def sumSquares(a: Int, b: Int) : Int = + if (a > b) 0 else square(a) + sumSquares(a + 1, b) + +sumSquares(2, 6) + + +// sum factorials +def fact(n: Int) : Int = + if (n == 0) 1 else n * fact(n - 1) + +def sumFacts(a: Int, b: Int) : Int = + if (a > b) 0 else fact(a) + sumFacts(a + 1, b) + +sumFacts(2, 6) + + + +// You can see the pattern....can we simplify out work? +// The type of functions from ints to ints: Int => Int + +def sum(f: Int => Int, a: Int, b: Int) : Int = { + if (a > b) 0 + else f(a) + sum(f, a + 1, b) +} + + +def sumSquares(a: Int, b: Int) : Int = sum(square, a, b) +def sumFacts(a: Int, b: Int) : Int = sum(fact, a, b) + +// What should we do for sumInts? + +def id(n: Int) : Int = n +def sumInts(a: Int, b: Int) : Int = sum(id, a, b) + + + +// Anonymous Functions: You can also write: + +def sumCubes(a: Int, b: Int) : Int = sum(x => x * x * x, a, b) +def sumSquares(a: Int, b: Int) : Int = sum(x => x * x, a, b) +def sumInts(a: Int, b: Int) : Int = sum(x => x, a, b) + + +// other function types +// +// f1: (Int, Int) => Int +// f2: List[String] => Option[Int] +// ... + + +// Function Composition +//====================== + +// How could Higher-Order Functions and Options be helpful? + +def add_footer(msg: String) : String = msg ++ " - Sent from iOS" + +def valid_msg(msg: String) : Boolean = msg.size <= 140 + +def duplicate(s: String) : String = s ++ s + +// they compose nicely +valid_msg(add_footer("Hello World")) +valid_msg(duplicate(add_footer("Hello World"))) + + +// first_word: let's first do it the ugly Java way using null: + +def first_word(msg: String) : String = { + val words = msg.split(" ") + if (words(0) != "") words(0) else null +} + +duplicate(first_word("Hello World")) +duplicate(first_word("")) + +def extended_duplicate(s: String) : String = + if (s != null) s ++ s else null + +extended_duplicate(first_word("")) + + +// Avoid always null! +def better_first_word(msg: String) : Option[String] = { + val words = msg.split(" ") + if (words(0) != "") Some(words(0)) else None +} + +better_first_word("Hello World").map(duplicate) +better_first_word("Hello World").map(duplicate).map(duplicate).map(valid_msg) + +better_first_word("").map(duplicate) +better_first_word("").map(duplicate).map(valid_msg) + + + // Type abbreviations //==================== // some syntactic convenience + type Pos = (int, Int) - type Board = List[List[Int]] -// Implicits -//=========== +// Implicits (Cool Feature) +//========================= // -// for example adding your own methods to Strings: -// imagine you want to increment strings, like +// 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 +// using implicit conversions. implicit class MyString(s: String) { @@ -123,10 +341,11 @@ "HAL".increment -// No return in Scala + +// No returns in Scala //==================== -//You should not use "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 @@ -136,20 +355,21 @@ def sq2(x: Int): Int = return x * x def sumq(ls: List[Int]): Int = { - (for (x <- ls) yield (return x * x)).sum[Int] + ls.map(sq1).sum[Int] } -sumq(List(1,2,3,4)) +sumq(List(1, 2, 3, 4)) -// last expression in a function is the return statement -def square(x: Int): Int = { - println(s"The argument is ${x}.") - x * x + +def sumq(ls: List[Int]): Int = { + val sqs : List[Int] = for (x <- ls) yield (return x * x) + sqs.sum } + // Pattern Matching //================== @@ -179,7 +399,6 @@ - def my_flatten(lst: List[Option[Int]]): List[Int] = lst match { case Nil => Nil case None::xs => my_flatten(xs) @@ -211,47 +430,34 @@ println(season("foobar")) + // User-defined Datatypes //======================== -abstract class Tree -case class Node(elem: Int, left: Tree, right: Tree) extends Tree -case class Leaf() extends Tree - +abstract class Colour +case class Red() extends Colour +case class Green() extends Colour +case class Blue() extends Colour -def 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)) +def fav_colour(c: Colour) : Boolean = c match { + case Red() => false + case Green() => true + case Blue() => false } -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 -} +// actually this can be written with "object" -def balance(tr: Tree): Int = tr match { - case Leaf() => 0 - case Node(_, left, right) => depth(left) - depth(right) -} - -balance(insert(t1, 3)) - // another example +//================= abstract class Person case class King() extends Person case class Peer(deg: String, terr: String, succ: Int) extends Person case class Knight(name: String) extends Person case class Peasant(name: String) extends Person -case class Clown() extends Person + def title(p: Person): String = p match { case King() => "His Majesty the King" @@ -260,6 +466,7 @@ case Peasant(name) => name } + def superior(p1: Person, p2: Person): Boolean = (p1, p2) match { case (King(), _) => true case (Peer(_,_,_), Knight(_)) => true @@ -281,95 +488,48 @@ -// Higher-Order Functions -//======================== - -// functions can take functions as arguments - -val lst = (1 to 10).toList - -def even(x: Int): Boolean = x % 2 == 0 -def odd(x: Int): Boolean = x % 2 == 1 - -lst.filter(x => even(x)) -lst.filter(even(_)) -lst.filter(even) - -lst.find(_ > 8) - -def square(x: Int): Int = x * x - -lst.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 => Int - -def 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 factorial -def 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.ListBuffer -def collatz_max(bnd: Long): (Long, Long) = { - val colNos = ListBuffer[(Long, Long)]() - for (i <- (1L to bnd).toList) colNos += ((collatz(i), i)) - colNos.max -} +// Problems with mutability and parallel computations +//==================================================== -def collatz_max(bnd: Long): (Long, Long) = { - (1L to bnd).map((i) => (collatz(i), i)).maxBy(_._1) +def count_intersection(A: Set[Int], B: Set[Int]) : Int = { + var count = 0 + for (x <- A; if (B contains x)) count += 1 + count } -//views -> lazy collection -def collatz_max(bnd: Long): (Long, Long) = { - (1L to bnd).view.map((i) => (collatz(i), i)).maxBy(_._1) +val A = (1 to 1000).toSet +val B = (1 to 1000 by 4).toSet + +count_intersection(A, B) + +// but do not try to add .par to the for-loop above + + +//propper parallel version +def count_intersection2(A: Set[Int], B: Set[Int]) : Int = + A.par.count(x => B contains x) + +count_intersection2(A, B) + + +//for measuring time +def time_needed[T](n: Int, code: => T) = { + val start = System.nanoTime() + for (i <- (0 to n)) code + val end = System.nanoTime() + (end - start) / 1.0e9 } -// raises a GC exception -(1 to 1000000000).filter(_ % 2 == 0).take(10).toList -// ==> java.lang.OutOfMemoryError: GC overhead limit exceeded +val A = (1 to 1000000).toSet +val B = (1 to 1000000 by 4).toSet -(1 to 1000000000).view.filter(_ % 2 == 0).take(10).toList +time_needed(10, count_intersection(A, B)) +time_needed(10, count_intersection2(A, B)) + + @@ -399,8 +559,6 @@ val indexes = (0 to 8).toList - - def empty(game: String) = game.indexOf(EmptyValue) def isDone(game: String) = empty(game) == -1 def emptyPosition(game: String) = (empty(game) % MaxValue, empty(game) / MaxValue) @@ -409,6 +567,9 @@ 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)) +get_row(game0, 3) +get_col(game0, 0) + def get_box(game: String, pos: Pos): List[Char] = { def base(p: Int): Int = (p / 3) * 3 val x0 = base(pos._1) @@ -417,28 +578,32 @@ (x0 until x0 + 3).toList.flatMap(x => ys.map(y => game(x + y * MaxValue))) } +get_box(game0, (0, 0)) +get_box(game0, (1, 1)) +get_box(game0, (2, 1)) -//get_row(game0, 0) -//get_row(game0, 1) -//get_box(game0, (3,1)) - +// 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) +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 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 + else { + val cs = candidates(game, emptyPosition(game)) + cs.map(c => search(update(game, empty(game), c))).toList.flatten + } } +search(game0).map(pretty) val game1 = """23.915... |...2..54. @@ -450,8 +615,9 @@ |.16..7... |...329..1""".stripMargin.replaceAll("\\n", "") +search(game1).map(pretty) -// game that is in the hard category +// game that is in the hard(er) category val game2 = """8........ |..36..... |.7..9.2.. @@ -474,8 +640,8 @@ |9724...5.""".stripMargin.replaceAll("\\n", "") -search(game0).map(pretty) -search(game1).map(pretty) +search(game2).map(pretty) +search(game3).map(pretty) // for measuring time def time_needed[T](i: Int, code: => T) = { @@ -487,8 +653,8 @@ search(game2).map(pretty) search(game3).distinct.length -time_needed(3, search(game2)) -time_needed(3, search(game3)) +time_needed(1, search(game2)) +time_needed(1, search(game3))