Functions As Objects Literal (“anonymous”) functions This is a literal number: 3.1416 This is a literal string: "Hello there!" This is a literal function: (x: Int, y: Int) => x – y The following are equivalent: It’s called “anonymous” because we didn’t bother to give it a name val subtract = (x: Int, y: Int) => x – y def subtract(x: Int, y: Int) = x – y Changes: No def, no name, the = changes to => (a “rocket”) Functions are values, just like numbers are values They can be saved in variables They can be passed to methods as arguments They can be returned from methods as results 2 Example use of functions scala> def isEven(x: Int) = x % 2 == 0 isEven: (x: Int)Boolean scala> val isOdd = (x: Int) => x % 2 != 0 isOdd: Int => Boolean = <function1> scala> def printPassingNumbers(predicate: Int => Boolean) { | for (i <- 1 to 10 if predicate(i)) print(i + " ") | println | } printPassingNumbers: (predicate: Int => Boolean)Unit scala> printPassingNumbers(isEven) 2 4 6 8 10 scala> printPassingNumbers(isOdd) 1 3 5 7 9 3 When to use anonymous functions The body of an anonymous function can be a compound expression (enclosed in { }) scala> printPassingNumbers((n: Int) => { | var prime = true | for (i <- 2 to n - 1 if n % i == 0) prime = false | prime | }) 1 2 3 5 7 This isn’t particularly easy to read The ideal use of an anonymous function is when: The function is short and simple You only need it in one or maybe two places 4 foreach foreach is an expression that applies a one-argument function to each value in a sequence (list, vector,…) Because foreach is a “binary” method, you can use operator notation scala> for (i <- List("one", "two")) print(i) onetwo scala> List("one", "two").foreach(print) onetwo scala> Vector("one", "two").foreach(print) onetwo scala> List("one", "two") foreach print onetwo The value of a foreach expression is Unit 5 map map applies a function to each element of a sequence, and returns a sequence of results scala> (1 to 10) map ((x: Int) => x * x) res27: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 4, 9, 16, 25, 36, 49, 64, 81, 100) scala> (1 to 10).toList map ((x: Int) => x * x) res28: List[Int] = List(1, 4, 9, 16, 25, 36, 49, 64, 81, 100) When possible, map returns the same type of sequence as it was given 6 filter filter applies a predicate (test) to each element of a sequence, and returns a sequence of those values that pass the test scala> (1 to 10) filter ((x: Int) => x % 3 == 0) res29: scala.collection.immutable.IndexedSeq[Int] = Vector(3, 6, 9) When possible, filter returns the same type of sequence as it was given 7 reduce reduce applies a binary operation between each pair of values, returning a single value as the result scala> (1 to 10) reduce ((x: Int, y: Int) => x + y) res31: Int = 55 scala> List(3, 1, 4, 1, 5, 9, 2, 6, 5) reduce ((x: Int, y: Int) => if (x > y) x else y res30: Int = 9 8 What’s the point? Scala provides a lot of higher-order functions that take functions as arguments Using these can make your code much shorter and easier to read scala> List(3, 1, 4, 1, 5, 9, 2, 6, 5) exists ((x: Int) => x > 5) res33: Boolean = true scala> List(3, 1, 4, 1, 5, 9, 2, 6, 5) forall ((x: Int) => x > 5) res34: Boolean = false The End