Getting Functional

Download Report

Transcript Getting Functional

Getting Functional
Object-Oriented Programming in Scala


Scala is object-oriented, and is based on Java’s model
An object is a singleton object (there is only one of it)




Variables and methods in an object are somewhat similar to Java’s
static variables and methods
Reference to an object’s variables and methods have the syntax
ObjectName.methodOrVariableName
The name of an object should be capitalized
A class may take parameters, and may describe any number of
objects


The class body is the constructor, but you can have additional constructors
With correct use of val and var, Scala provides getters and setters for
class parameters
2
Functional Programming (FP) in Scala

In FP,

Functions are first-class objects. That is, they are values, just like other objects are
values, and can be treated as such



Functions should only transform their inputs into their outputs



Functions can be assigned to variables, passed as parameters to higher-order functions,
returned as results of functions
There is some way to write function literals
A function should have no side effects
 It should not do any input/output
 It should not change any state (any external data)
Given the same inputs, a function should produce the same outputs, every time--it
is deterministic
If a function is side-effect free and deterministic, it has referential
transparency—all calls to the function could be replaced in the program text by
the result of the function

But we need random numbers, date and time, etc.
3
Creating Lists










scala> List('a', 'b', 'c')
res0: List[Char] = List(a, b, c)
scala> "abc" toList
res1: List[Char] = List(a, b, c)
scala> "Welcome to Scala" split(" ")
res2: Array[java.lang.String] = Array(Welcome, to, Scala)
scala> val scala = "Scala" toList
scala: List[Char] = List(S, c, a, l, a)
scala> "Hello" :: scala
res3: List[Any] = List(Hello, S, c, a, l, a)
4
Nil and ::












scala> List()
res4: List[Nothing] = List()
scala> Nil
res5: scala.collection.immutable.Nil.type = List()
scala> List() == Nil
res6: Boolean = true
scala> List[String]()
res7: List[String] = List()
scala> "xyz" :: Nil
res8: List[java.lang.String] = List(xyz)
scala> "abc" :: "xyz" :: Nil
res9: List[java.lang.String] = List(abc, xyz)
5
head, tail, and isEmpty












scala> val penn = "Pennsylvania" toList
penn: List[Char] = List(P, e, n, n, s, y, l, v, a, n, i, a)
scala> penn head
res10: Char = P
scala> penn tail
res11: List[Char] = List(e, n, n, s, y, l, v, a, n, i, a)
scala> penn isEmpty
res12: Boolean = false
scala> Nil isEmpty
res13: Boolean = true
scala> Nil head
java.util.NoSuchElementException: head of empty list (plus many more lines!)
6
take, drop, and splitAt










scala> penn
res16: List[Char] = List(P, e, n, n, s, y, l, v, a, n, i, a)
scala> penn take 4
res17: List[Char] = List(P, e, n, n)
scala> penn drop 4
res18: List[Char] = List(s, y, l, v, a, n, i, a)
scala> penn splitAt 4
res19: (List[Char], List[Char]) = (List(P, e, n, n),List(s, y, l, v, a, n, i, a))
scala> penn.splitAt(4)
res20: (List[Char], List[Char]) = (List(P, e, n, n),List(s, y, l, v, a, n, i, a))
7
toString and mkString












scala> List(1, 2, 3).toString
res25: String = List(1, 2, 3)
scala> List(1, 2, 3).toString == "List(1, 2, 3)"
res26: Boolean = true
scala> List(1, 2, 3) mkString(" is less than ")
res27: String = 1 is less than 2 is less than 3
scala> List(1, 2, 3) mkString("*")
res28: String = 1*2*3
scala> List(1, 2, 3) mkString("<: ", "--", " :>")
res29: String = <: 1--2--3 :>
scala> List(1, 2, 3) mkString("(", ", ", ")")
res30: String = (1, 2, 3)
8
zip and unzip












scala> val words = "one two three" split " "
words: Array[java.lang.String] = Array(one, two, three)
scala> val numbers = List(1, 2, 3, 4, 5)
numbers: List[Int] = List(1, 2, 3, 4, 5)
scala> val z = words zip numbers
z: Array[(java.lang.String, Int)] = Array((one,1), (two,2), (three,3))
scala> val zz = numbers zip words
zz: List[(Int, java.lang.String)] = List((1,one), (2,two), (3,three))
scala> z toMap
res31: scala.collection.immutable.Map[java.lang.String,Int] = Map((one,1), (two,2),
(three,3))
scala> zz unzip
res32: (List[Int], List[java.lang.String]) = (List(1, 2, 3),List(one, two, three))
9
Higher-order functions








The basic syntax of a function literal is
parameter_list => function_body
A higher-order function is one that takes a function as a parameter, or returns a function as a result
scala> val brag = "Scala is great!" toList

brag: List[Char] = List(S, c, a, l, a, , i, s, , g, r, e, a, t, !)
scala> brag count((ch: Char) => ch == 'a')

res34: Int = 3
scala> brag count((ch: Char) => !(ch isLetter))

res35: Int = 3
scala> "Scala is great!".toList.count((ch: Char) => ch < 'f')

res36: Int = 9
scala> "aeiou" contains 'e'

res37: Boolean = true
scala> "Scala is great!".toList.count((ch: Char) => "aeiou" contains ch)

res38: Int = 5
10
Abbreviations

In a literal function, you can usually omit the type (and, if there’s only one parameter,
the parentheses)




In fact, if there is only one parameter, used once, you can omit the parameter and the
=> and just use _ to stand in for the parameter


scala> brag
res40: List[Char] = List(S, c, a, l, a, , i, s, , g, r, e, a, t, !)
scala> brag count((ch: Char) => ch == 'a')
res41: Int = 3
scala> brag count (ch => ch == 'a')
res42: Int = 3
scala> brag count (_ == 'a')
res44: Int = 3
You can use underscores to stand for multiple parameters, provided each is used once,
and they are used in order

scala> (1 to 10).foldLeft(0)(_ + _)
res27: Int = 55
11
sortWith

scala> brag sortWith((x, y) => x < y)
res49: List[Char] = List( , , !, S, a, a, a, c, e, g, i, l, r, s, t)

Since there are two parameters, we can use two underscores


scala> brag sortWith (_ < _)
res50: List[Char] = List( , , !, S, a, a, a, c, e, g, i, l, r, s, t)

Order matters!



scala> brag sortWith (_ > _)
res52: List[Char] = List(t, s, r, l, i, g, e, c, a, a, a, S, !, , )
12
forall and exists











Whereas count returns an Int, forall and exists return a Boolean
scala> val n = List(3, 1, 4, 1, 6)
n: List[Int] = List(3, 1, 4, 1, 6)
scala> n forall(x => x < 8)
res53: Boolean = true
scala> n forall(x => x < 5)
res54: Boolean = false
scala> n exists(_ < 5)
res55: Boolean = true
scala> n exists(_ > 8)
res56: Boolean = false
13
foreach








foreach returns the (uninteresting) Unit value
scala> val brag = List("Scala", "is", "great!")
brag: List[java.lang.String] = List(Scala, is, great!)
scala> brag foreach (println(_))
Scala
is
great!
scala> brag foreach(println)
Scala
is
great!
scala> List(3, 1, 4, 1, 6) foreach (x => if (x > 1) println(x))
3
4
6
14
Least upper bound

The following list contains only integers:


Now let’s add a non-integer to it


scala> val list = List(1, 2, 3)
list: List[Int] = List(1, 2, 3)
scala> "hello" :: list
res0: List[Any] = List(hello, 1, 2, 3)
The type of this new list is the lowest class in the hierarchy that is
a superclass of all the elements of the list
15
Closures












scala> var c = 5
c: Int = 5
scala> val mult = (x: Int) => c * x
mult: (Int) => Int = <function1>
scala> mult(3)
res63: Int = 15
scala> c = 7
c: Int = 7
scala> mult(10)
res64: Int = 70
What will happen if we pass the mult function into another function in another context?
It continues to “link to” (enclose?) the original variable c, not it’s value
16
map







map produces a new list by applying the given function to each element of the
given list
scala> val n = List(1, 2, 3, 4, 5)
n: List[Int] = List(1, 2, 3, 4, 5)
scala> n map(x => x * x)
res65: List[Int] = List(1, 4, 9, 16, 25)
scala> n map (_ >= 3)
res66: List[Boolean] = List(false, false, true, true, true)
17
flatMap







flatMap produces a new list by applying the given function to each element of the
given list, and “flattening” the result
scala> n
res70: List[Int] = List(1, 2, 3, 4, 5)
scala> n map (x => List(x, x*x))
res72: List[List[Int]] = List(List(1, 1), List(2, 4), List(3, 9), List(4, 16), List(5,
25))
scala> n flatMap(x => List(x, x*x))
res73: List[Int] = List(1, 1, 2, 4, 3, 9, 4, 16, 5, 25)
18
filter







filter produces a new list consisting of the values that pass the given test
scala> n
res67: List[Int] = List(1, 2, 3, 4, 5)
scala> n filter (_ < 4)
res68: List[Int] = List(1, 2, 3)
scala> n filter (_ % 2 == 1)
res69: List[Int] = List(1, 3, 5)
19
foldl, foldr

The “fold” functions apply a binary operator to the values in a
list, pairwise, starting from the left or starting from the right





scala> val list = List(10, 1, 2, 3)
list: List[Int] = List(10, 1, 2, 3)
scala> list.foldLeft(0)(_ - _)
res3: Int = -16
scala> list.foldRight(0)(_ - _)
res4: Int = 8
scala> ((((0 - 10) - 1) - 2) - 3)
res6: Int = -16
scala> (10 - (1 - (2 - (3 - 0))))
res8: Int = 8
Some Map methods


These methods have little to do with “being functional,” so think of them as a bonus :-)
We’re going to use a map that takes small numbers (1..5) to their squares:

scala> val m = Map(1 -> 1, 2 -> 4, 3 -> 9, 4 -> 16, 5 -> 25)
m: scala.collection.immutable.Map[Int,Int] = Map((5,25), (1,1), (2,4),
(3,9), (4,16))





scala> m(4)
res1: Int = 16
scala> m(10)
java.util.NoSuchElementException: key not found: 10
...and many more lines of error message
scala> m.get(4)
res3: Option[Int] = Some(16)
scala> m.get(10)
res4: Option[Int] = None
scala> m.get(4) match {
| case Some(x) => x
| case None => -999
|}
res5: Int = 16
21
Another Map method



scala> m
res6: scala.collection.immutable.Map[Int,Int] = Map((5,25), (1,1),
(2,4), (3,9), (4,16))
scala> m.getOrElse(4, -999)
res7: Int = 16
scala> m.getOrElse(10, -999)
res8: Int = -999
22
“Houston, we have a problem.”



scala> val x = m.getOrElse(4, "Hello")
x: Any = 16
That’s pretty scary--but it gets worse:
scala> x + 1
<console>:8: error: type mismatch;
found : Int(1)
required: String
x+1
^
23
The End
If I were to pick a language to use today
other than Java, it would be Scala.
--James Gosling, creator of Java
24