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 It’s called “anonymous” because we didn’t bother to give it a name The following are equivalent: 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
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
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
foreach foreach is an expression that applies a one-argument function to each value in a sequence (list, vector,…) scala> for (i <- List("one", "two")) print(i) onetwo scala> List("one", "two").foreach(print) onetwo scala> Vector("one", "two").foreach(print) onetwo Because foreach is a “binary” method, you can use operator notation scala> List("one", "two") foreach print onetwo The value of a foreach expression is Unit
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
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
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
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