Functions and Methods Definitions and types A function is a piece of code that takes arguments and returns a result A pure function.
Download
Report
Transcript Functions and Methods Definitions and types A function is a piece of code that takes arguments and returns a result A pure function.
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.doubleName
res4: 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 + 2
res0: 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 3
res3: Boolean = true
4
Function types
Functions are objects, and every object has a type
The type of
(name: String) => name + name.reverse
is 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
And then they can be called by name
scala> val foo = (n: Int) => n * (n - 1)
foo: Int => Int = <function1>
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 addS
res0: 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 addS
res2: 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>
res12:
scala>
res13:
scala>
(1 to 10) reduce ((x: Int, y: Int) => x + y)
Int = 55
(1 to 10) reduce ((x: Int, y: Int) => x * y)
Int = 3628800
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>
res16:
scala>
res17:
scala>
res21:
(1 to 10).sum
Int = 55
(1 to 10).product
Int = 3628800
List("one", "two", "three").mkString
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
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)
res0: List[Int] = List(4, 5, 9)
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'.toInt
res4: 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.isEmpty
res5: Boolean = false
scala> abc.isDefined
res6: 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 others
19
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> v
res29: 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 practice
21
The End
22