// Scala Lecture 2+ −
//=================+ −
+ −
+ −
// the pain with 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; if (e % 2) == 0; if (e != 4)) 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),+ −
// for 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:+ −
+ −
val test = + −
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 Scala you use Option+ −
// - if the value is present, you use Some(value)+ −
// - if no value is present, you use None+ −
+ −
+ −
List(7,24,3,4,5,6).find(_ < 4)+ −
List(5,6,7,8,9).find(_ < 4)+ −
+ −
List(7,2,3,4,5,6).filter(_ < 4)+ −
+ −
// some operations on Option's+ −
+ −
val lst = List(None, Some(1), Some(2), None, Some(3))+ −
+ −
lst.flatten+ −
+ −
Some(10).get+ −
None.get+ −
+ −
Some(1).isDefined+ −
None.isDefined+ −
+ −
val 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)+ −
}+ −
+ −
// 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 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+ −
+ −
val my_url = """https://nms.kcl.ac.uk/christian.urban"""+ −
//val my_url = """https://nms.kcl.ac.uk/christan.urban""" // misspelled+ −
+ −
Source.fromURL(my_url).mkString+ −
+ −
Try(Source.fromURL(my_url).mkString).getOrElse("")+ −
+ −
Try(Some(Source.fromURL(my_url).mkString)).getOrElse(None)+ −
+ −
+ −
// a function that turns strings into numbers+ −
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 = (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+ −
// 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 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(42)+ −
+ −
+ −
// ... how are we supposed to know that this returns -1+ −
+ −
+ −
// 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) && odd(x))+ −
lst.filter(even(_))+ −
lst.filter(odd && even)+ −
+ −
lst.find(_ > 8)+ −
+ −
// map applies a function to each element of a list+ −
+ −
def square(x: Int): Int = x * x+ −
+ −
val lst = (1 to 10).toList+ −
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).filter(_ > 4)+ −
+ −
+ −
val l = List((1, 3),(2, 4),(4, 1),(6, 2))+ −
+ −
l.map(square(_._1))+ −
+ −
+ −
// Why are functions as arguments 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)+ −
+ −
+ −
sumInts(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 our 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)+ −
+ −
sumInts(10, 12)+ −
+ −
+ −
// 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]+ −
// ... + −
+ −
+ −
// an aside: partial application+ −
+ −
def add(a: Int)(b: Int) : Int = a + b+ −
def add_abc(a: Int)(b: Int)(c: Int) : Int = a + b + c+ −
+ −
val add2 : Int => Int = add(2)+ −
add2(5)+ −
+ −
val add2_bc : Int => Int => Int = add_abc(2) + −
val add2_9_c : Int => Int = add2_bc(9) + −
+ −
add2_9_c(10)+ −
+ −
sum(add(2), 0, 2)+ −
sum(add(10), 0, 2)+ −
+ −
+ −
// Function Composition+ −
//======================+ −
+ −
// How can be 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 very nicely, e.g+ −
+ −
valid_msg(add_footer("Hello World"))+ −
valid_msg(duplicate(duplicate(add_footer("Helloooooooooooooooooo World"))))+ −
+ −
// but not all functions do+ −
// 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(""))+ −
+ −
// but this is against the rules of the game: we do not want+ −
// to change duplicate, because first_word might return null+ −
+ −
+ −
// 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)+ −
better_first_word("").map(duplicate).map(duplicate).map(valid_msg)+ −
+ −
better_first_word("").map(duplicate)+ −
better_first_word("").map(duplicate).map(valid_msg)+ −
+ −
+ −
+ −
+ −
+ −
// Problems with mutability and parallel computations+ −
//====================================================+ −
+ −
def count_intersection(A: Set[Int], B: Set[Int]) : Int = {+ −
var count = 0+ −
for (x <- A; if (B contains x)) count += 1 + −
count+ −
}+ −
+ −
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,+ −
// otherwise you will be caught in race-condition hell.+ −
+ −
+ −
//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+ −
}+ −
+ −
val A = (1 to 1000000).toSet+ −
val B = (1 to 1000000 by 4).toSet+ −
+ −
time_needed(10, count_intersection(A, B))+ −
time_needed(10, count_intersection2(A, B))+ −
+ −
+ −
+ −
+ −
+ −
+ −
// No returns 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 * x+ −
def sq2(x: Int): Int = return x * x+ −
+ −
def sumq(ls: List[Int]): Int = {+ −
ls.map(sq1).sum[Int]+ −
}+ −
+ −
sumq(List(1, 2, 3, 4))+ −
+ −
+ −
+ −
def sumq(ls: List[Int]): Int = {+ −
val sqs : List[Int] = for (x <- ls) yield (return x * x)+ −
sqs.sum+ −
}+ −
+ −
+ −
+ −
+ −
// Type abbreviations+ −
//====================+ −
+ −
// some syntactic convenience+ −
+ −
type Pos = (int, Int)+ −
type Board = List[List[Int]]+ −
+ −
+ −
+ −
+ −
// Sudoku in Scala+ −
//=================+ −
+ −
// THE POINT OF THIS CODE IS NOT TO BE SUPER+ −
// EFFICIENT AND FAST, just explaining exhaustive+ −
// depth-first search+ −
+ −
+ −
val 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 = 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) = + −
(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))+ −
+ −
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)+ −
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_box(game0, (0, 0))+ −
get_box(game0, (1, 1))+ −
get_box(game0, (2, 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))+ −
+ −
//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 {+ −
val cs = candidates(game, emptyPosition(game))+ −
cs.par.map(c => search(update(game, empty(game), c))).toList.flatten+ −
}+ −
}+ −
+ −
search(game0).map(pretty)+ −
+ −
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", "")+ −
+ −
search(game1).map(pretty)+ −
+ −
// game that is in the hard(er) category+ −
val game2 = """8........+ −
|..36.....+ −
|.7..9.2..+ −
|.5...7...+ −
|....457..+ −
|...1...3.+ −
|..1....68+ −
|..85...1.+ −
|.9....4..""".stripMargin.replaceAll("\\n", "")+ −
+ −
// 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", "")+ −
+ −
+ −
search(game2).map(pretty)+ −
search(game3).map(pretty)+ −
+ −
// for measuring time+ −
def 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.length+ −
time_needed(1, search(game2))+ −
time_needed(1, search(game3))+ −
+ −
+ −
+ −
+ −
//===================+ −
// the end for today+ −