functions and methods. definitions and types a function is a piece of code that takes arguments and...
TRANSCRIPT
Functions and Methods
Definitions and types
A function is a piece of code that takes arguments and returns a result A pure function is a function whose result depends only on
its arguments You call a function If called again with the same arguments, a pure function
will return the same result A method is a function that belongs to an object
You “talk” to the object and ask it to run its method More formally, you send a message to the object
2
Syntax Method (in a class or object): def name (args) = expression
scala> class Person(name: String) { | def doubleName = name * 2 | }defined class Person
scala> val jo = new Person("Jo")jo: Person = Person@1de9c46d
scala> jo.doubleNameres4: String = JoJo
Function: (args) => expression scala> val mirror = (name: String) => name + name.reverse
mirror: String => String = <function1>
scala> mirror("Dave")res6: String = DaveevaD
3
Operators and methods
Reminder: Operators are really methods scala> 2 + 2res0: Int = 4
scala> 2 .+(2)res1: Int = 4
Binary (object + one argument) methods can be treated like operators
scala> List(1, 2, 3, 4).contains(3)res2: Boolean = true
scala> List(1, 2, 3, 4) contains 3res3: Boolean = true
4
Function types
Functions are objects, and every object has a type The type of(name: String) => name + name.reverseis String => String
scala> (s: String, from: Int, to: Int) => s.drop(from).take(to - from + 1)res4: (String, Int, Int) => String = <function3>
scala> res4("paper", 1, 3)res5: String = ape
So the type of a function is(types of its arguments) => type of its result
5
Anonymous functions
Functions can be given names scala> val foo = (n: Int) => n * (n - 1)foo: Int => Int = <function1>
And then they can be called by name scala> foo(5)res1: Int = 20
But they don’t have to have a name to be called scala> ((n: Int) => n * (n - 1))(5)res2: Int = 20
6
Higher-order functions
A higher-order function is one that either takes one or more functions as arguments, or returns a function as a result, or both
Scala provides a number of higher-order functions, including the “big three”: map, filter, and reduce
map applies a function to each element of a sequence, returning a sequence of the results
filter applies a predicate (Boolean function) to each element of a sequence, and returns a sequence of those elements that satisfy the predicate (the predicate returns true)
reduce repeatedly applies a binary operation to pairs of elements of a sequence, returning a single value
map map applies a function to each element of a sequence, returning a
sequence of the results scala> Array(1, 2, 3).map((x: Int) => x * x)
res11: Array[Int] = Array(1, 4, 9) scala> Vector(1, 2, 3) map ((x: Int) => x * x)
res12: scala.collection.immutable.Vector[Int] = Vector(1, 4, 9) scala> List(1, 2, 3) map ((x: Int) => x * x)
res13: List[Int] = List(1, 4, 9) scala> Range(1, 6) map ((x: Int) => x * x)
res15: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 4, 9, 16, 25)
scala> (1 until 6) map ((x: Int) => x * x)res16: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 4, 9, 16, 25)
scala> "boogie" map ((ch: Char) => "aeiou" contains ch)res17: scala.collection.immutable.IndexedSeq[Boolean] = Vector(false, true, true, false, true, true)
8
More about map scala> def addS(str: String) = str + "s"
addS: (str: String)String
scala> List("dog", "cat", "horse") map addSres0: List[String] = List(dogs, cats, horses)
scala> List(1, 2, 3) map addS<console>:9: error: type mismatch; found : String => String required: Int => ? List(1, 2, 3) map addS ^
scala> def addS(x: Any) = x + "s"addS: (x: Any)String
scala> List(1, 2, 3) map addSres2: List[String] = List(1s, 2s, 3s)
9
filter filter applies a predicate (Boolean function) to each element
of a sequence, and returns a sequence of those elements that satisfy the predicate (the predicate returns true) scala> List(3, 1, 4, 1, 5, 9) filter ((x: Int) => x >
3)res0: List[Int] = List(4, 5, 9)
scala> "University" filter ((ch: Char) => ch > 'm')res1: String = nvrsty
scala> (2 to 20) filter ((x: Int) => isPrime(x))res9: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 3, 5, 7, 11, 13, 17, 19)
scala> "Scala is a good language".split(" ") filter ((w: String) => w.length >= 5)res10: Array[String] = Array(Scala, language)
10
reduce reduce repeatedly applies a binary operation to pairs of
elements of a sequence, returning a single value scala> (1 to 10) reduce ((x: Int, y: Int) => x + y)
res12: Int = 55 scala> (1 to 10) reduce ((x: Int, y: Int) => x * y)
res13: Int = 3628800 scala> List("one", "two", "three") reduce ((x: String,
y: String) => x + y)res15: String = onetwothree
Of course, many of these are already supplied by Scala scala> (1 to 10).sum
res16: Int = 55 scala> (1 to 10).product
res17: Int = 3628800 scala> List("one", "two", "three").mkString
res21: String = onetwothree
Shortcuts When you define a method with def, you must specify the types of the
parameter With literal functions, if the parameter type is obvious, you usually don’t need
to specify it scala> List(3, 1, 4, 1, 5, 9) filter ((x) => x > 3)
res0: List[Int] = List(4, 5, 9) With a single parameter and an inferred type, you can also leave out the
parentheses scala> List(3, 1, 4, 1, 5, 9) filter (x => x > 3)
res1: List[Int] = List(4, 5, 9) In fact, with a single parameter whose type is obvious, you can leave out the
parameter name, and just use an underscore scala> List(3, 1, 4, 1, 5, 9) filter (_ > 3)
res2: List[Int] = List(4, 5, 9)
12
Splitting lists scala> "one two three" takeWhile ((ch: Char) => ch != ' ')
res0: String = one
scala> "one two three" takeWhile (_ != ' ')res1: String = one
scala> "one two three" dropWhile (_ != ' ')res2: String = " two three"
scala> "one two three" span (_ != ' ')res3: (String, String) = (one," two three")
scala> "one two three" partition (_ != ' ')res4: (String, String) = (onetwothree," ")
scala> List(3, 5, 6, 8, 9) partition (_ % 2 == 0)res4: (List[Int], List[Int]) = (List(6, 8),List(3, 5, 9))
13
Testing all elements
sequence.forall(predicate) checks if every element of the sequence satisfies the predicate
scala> List(1, 2, 3) forall (_ > 0)res0: Boolean = true
scala> List(1, -2, 3) forall (_ > 0)res1: Boolean = false
sequence.exists(predicate) checks if any element of the sequence satisfies the predicate
scala> List(1, 2, 3) exists (_ < 0)res2: Boolean = false
scala> List(1, -2, 3) exists (_ < 0)res3: Boolean = true
14
Extreme underscores
If you have more than one parameter, you can sometimes use an underscore for each The first underscore stands for the first parameter, the second
underscore for the second parameter, etc. scala> List(5, 3, 4, 2, 1) sortWith (_ < _)res1: List[Int] = List(1, 2, 3, 4, 5)
scala> "This is a list of words".split(" ") sortWith (_.length < _.length)res2: Array[String] = Array(a, is, of, This, list, words)
15
find list.find(predicate) returns, as Some(value), the first value
in the sequence that satisfies the predicate, or None if no such value is found
scala> List(3, 1, 4, 1, 6) find (_ > 3)res5: Option[Int] = Some(4)
scala> List(3, 1, 4, 1, 6) find (_ > 7)res6: Option[Int] = None
scala> "Read the assignment carefully".split(" ") find (_.length > 6)res7: Option[String] = Some(assignment)
I’ll review Option in just a moment
16
find with Strings scala> val digits = Math.PI.toString
digits: String = 3.141592653589793
scala> List(3, 1, 4, 1, 6) find (_ > 3)res0: Option[Int] = Some(4)
scala> digits find (_ > 3)res1: Option[Char] = Some(3)
scala> digits find (_ > '3')res2: Option[Char] = Some(4)
scala> 3 == '3'res3: Boolean = false
scala> '3'.toIntres4: Int = 51
17
Working with an Option You can match on an Option
val opt = men find (x => isHonest(x))opt match { case Some(man) => println(s"$man is an honest man.") case None => println("Not found.") }
An Option is a collection (of zero or one thing), so you can use collection operations on it
scala> val abc = Some("abc")abc: Some[String] = Some(abc)
scala> abc.isEmptyres5: Boolean = false
scala> abc.isDefinedres6: Boolean = true
scala> for (a <- abc) println(a)abc
scala> abc getOrElse("xyz")res8: String = abc
18
foreach Unlike the previously discussed higher-order functions, the return value of
foreach is Unit, () foreach does something with each element of a sequence, and is used for its
side effects
scala> (1 to 10) foreach (x => print(x * x + " "))1 4 9 16 25 36 49 64 81 100
scala> var sum = 0; (1 to 10) foreach (x => sum += x * x)sum: Int = 385
Scala is “multi-paradigm”: It’s object-oriented and functional Functional languages don’t allow, or at least try to avoid, side effects The entire purpose of foreach is to have side effects! If you want to get side effects from a higher-order function, use foreach in
preference to any of the others19
for comprehensions
A for comprehension is a convenient way to combine a number of map and filter operations
scala> (1 to 20) filter (isPrime _) map (x => x * x)res1: scala.collection.immutable.IndexedSeq[Int] = Vector(4, 9, 25, 49, 121, 169, 289, 361)
scala> for (i <- 1 to 20 if isPrime(i)) yield (i * i)res2: scala.collection.immutable.IndexedSeq[Int] = Vector(4, 9, 25, 49, 121, 169, 289, 361)
20
Why higher-order functions? Use of higher-order functions makes code shorter and (usually) easier to read
With for comprension: scala> (1 to 10) map (x => x * x)res27: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 4, 9, 16, 25, 36, 49, 64, 81, 100)
Just as a loop: scala> var v: Vector[Int] = Vector()v: Vector[Int] = Vector()
scala> for (i <- 1 to 10) { | v = v :+ i * i | }
scala> vres29: Vector[Int] = Vector(1, 4, 9, 16, 25, 36, 49, 64, 81, 100)
Higher-order functions make certain tasks much easier Just like anything else, learning to use higher-order functions easily and
effectively takes practice21
The End
22