Tuples, Maps, Arrays, and Options

Options

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)
     }



Tuples

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)

Maps

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

Arrays

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:

·       http://www.scala-lang.org/api/2.12.1/scala/Array.html