An option is a
container object that is either empty—None-- or containing a single value of
some type T-- Some(value).
Sometimes, instead of throwing an exception a function will return
null if it can't compute an answer. This technique is used for situations that
don't rise to the level of an error. Of course the calling function must check
for a null return value.
In Scala, consider using options instead of returning null:
def milesToKilometers(miles:
Double): Option[Double] =
if (miles
< 0) None else Some(miles * 1.60934)
One nice feature of using options is that we can pull them apart using match expressions:
def kilometersToMeters(km:
Option[Double]): Option[Double] =
km match {
case None => None
case Some(x) => Some(x * 1000)
}
}
kilometersToMeters(milesToKilometers(65)) //> res0:
Option[Double] = Some(104607.1)
kilometersToMeters(milesToKilometers(-65)) //> res1:
Option[Double] = None
Here's a variant of milesToKilometers that works with optional parameters:
def milesToKilometers2(miles: Option[Double]): Option[Double] =
miles match {
case None => None
case Some(x) =>
milesToKilometers(x)
}
A tuple is a sequence of values:
scala> val record = ("Carl Larc", "male",
25)
record: (String, String, Int) = (Carl Larc,male,25)
We can access the components of a tuple:
scala> record._1
res31: String = Carl Larc
scala> record._2
res32: String = male
scala> record._3
res33: Int = 25
scala> record._4
<console>:9: error: value _4 is not a member of (String, String, Int)
record._4
A cool feature is a compound assignment that extracts the components of a tuple and assigns them to variables or constants:
scala> val (name, gender, age) = record
name: String = Carl Larc
gender: String = male
age: Int = 25
scala> name
res28: String = Carl Larc
scala> gender
res29: String = male
scala> age
res30: Int = 25
Tuples are useful for functions that need to return more than one value:
def toPolar(crt:
(Double, Double)) = {
val
(xc, yc) = crt
val
r = math.sqrt(xc * xc + yc * yc)
val
theta = math.acos(xc/yc)
(r, theta)
}
toPolar((1, 1)) //> res9: (Double, Double) = (1.4142135623730951,0.0)
toPolar((0, 1)) //> res10: (Double, Double) = (1.0,1.5707963267948966)
A map is a collection of key-value pairs, usually implemented as a tree or hash table.
scala> val gender = Map(1->"male",
2->"female", 3->"transgender",
4->"unspecified")
gender: scala.collection.immutable.Map[Int,String] = Map(1 -> male, 2 ->
female, 3 -> transgender, 4 -> unspecified)
scala> gender(3)
res0: String = transgender
scala> gender(0)
java.util.NoSuchElementException: key not found:
0
By default, maps are immutable. This means that once created, pairs can't be added, removed, or modified:
scala> gender(1) = "man"
<console>:9: error: value update is not a member of
scala.collection.immutable.Map[Int,String]
Here's how to declare a mutable map:
val scores =
collection.mutable.Map("smith"->90, "jones"->86, "simpson"->67)
//> scores :
scala.collection.mutable.Map[String,Int] = Map(smith -> 90, simpso
//| n -> 67, jones -> 86)
scores("smith") //> res3: Int = 90
scores("jones") //> res4: Int = 86
scores("hanson") //> java.util.NoSuchElementException:
key not found: hanson
Oops, we forgot poor Hanson. Not a problem for mutable maps:
scores += "hanson"->100 //> res5: mathdemo.scores.type = Map(smith -> 90, hanson
-> 100, simpson -> 67,
//| jones -> 86)
scores("hanson") //> res6: Int = 100
If a student drops the course, we can delete them from the map:
scores -= "jones" //> res7: mathdemo.scores.type
= Map(smith -> 90, hanson -> 100, simpson -> 67)
Smith is begging for 5 more points, here's how we handle it:
scores("smith") = scores("smith") + 5
Example: Concordance
The following code shows how to build a concordance from a text file. A concordance is a map of type Map[String, Int]. A member such as ("gadzooks", 3) says that the word "gadzooks" occurred three times in the document.
import scala.io.Source
object Concordance extends App {
var fTable = new
scala.collection.mutable.HashMap[String, Int] // our frequencey table
val wordPattern =
"[a-zA-Z]+".r // this is how to turn a string into a regular
expression
val source = Source.fromFile(args(0),
"UTF-8") // source file specified in command line
val lineIterator = source.getLines
for(line <- lineIterator) {
for(nextWord <-
wordPattern.findAllIn(line)) {
fTable(nextWord.toLowerCase) =
fTable.getOrElse(nextWord.toLowerCase, 0) + 1
}
}
for((k, v) <- fTable) println(k +
": " + v)
}
Notes:
table.getOrElse(key, default) = default if no value is associated with key
Here's a document:
to be or not to be,
that is the question.
A question to be sure.
I sure am thirsty.
May I ask for some ale?
Hello?
Was that the wrong question to ask?
Here's the concordance produced:
wrong: 1
am: 1
is: 1
not: 1
ask: 2
some: 1
or: 1
question: 3
be: 3
to: 4
sure: 2
that: 2
thirsty: 1
for: 1
a: 1
was: 1
ale: 1
the: 2
i: 2
hello: 1
may: 1
An array is an indexed sequence of contiguous variables.
Creating an array of values is simple:
scala> val days = Array("Monday",
"Tuesday", "Wednesday", "Thursday",
"Friday", "Saturday", "Sunday")
days: Array[String] = Array(Monday, Tuesday, Wednesday, Thursday, Friday,
Saturday, Sunday)
Like all sequences, we can iterate over arrays:
scala> for(day <- days) {
println(day)
}
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
Or we can iterate over the indices of an array:
scala> for(i <- 0 until days.length) {
println(days(i))
}
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
Notice that we use parentheses instead of square braces to access array elements.
Even though days is a constant array, its components are still variables that can be updated:
scala> days(2) = "Hump Day"
scala> days
res19: Array[String] = Array(Monday, Tuesday, Hump Day, Thursday, Friday,
Saturday, Sunday)
We can declare and fill an array in two steps:
scala> val squares = new Array[Int](10)
squares: Array[Int] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
scala> for(i <- 0 until 10) {
squares(i) = i * i
}
scala> for(square <- squares) println(square)
0
1
4
9
16
25
36
49
64
81
Scala has lots of array operations: