Knowing Scala: Exercise 1

Taking my own 15 Exercises to Know A Programming Language as a starting point for my exploration of Scala...

The first set of exercises are right up the alley of a language with pattern-matching and list-processing:

Write a program that takes as its first argument one of the words ‘sum,’ ‘product,’ ‘mean,’ or ‘sqrt’ and for further arguments a series of numbers. The program applies the appropriate function to the series.

The first thing I did was write the functions:

[sourcecode lang="scala"]
val args = List(1, 2, 3, 4, 5)
args.foldLeft(0)(_+_) //Sum
args.foldLeft(1)(_*_) //Product
args.sort(_ \< _)(if(args.length % 2 == 0) args.length / 2 - 1 else args.length / 2) //Median
args.map(Math.sqrt(_)) //Sqrt
[/sourcecode]

Here the list-processing functions (highlighted in red) shine. The foldLeft() family of functions, also known as reduce, inject, or aggregate, applies a predicate to the elements of a data structure, accumulating a value. It's hard to imagine cleaner code than what we have for the 'sum' and 'product' challenges. Similarly, the map() function is similarly clean-as-a-whistle for the 'sqrt' challenge. The 'median' challenge I'm less thrilled about: Passing in a custom comparator predicate to sort() is lovely, but my fingers typed in the C-language ternary operator :? and I was sorry to find that Scala doesn't have it.

A big win for Scala is that I typed these in the Read-Evaluate-Print-Loop aka interactive console rather than having to deal with the overhead of a 'real' program and a edit-compile-test loop.

Moving on, the complete program follows:

[sourcecode lang="scala"]
object Main
{
def main(args : Array[String])
{
val argList = List.fromArray(args)
val op = opMatch(argList.head)
val numList = argList.tail.map(java.lang.Double.parseDouble(_))
val result = op(numList)
print(result)
}

def sum(args : List[Double]) : Double = { args.foldLeft(0.0)(_+_) }

def product(args : List[Double]) : Double = { args.foldLeft(1.0)(_*_) }

def median(args : List[Double]) : Double =
{
args.sort(_ \< _)(if(args.length % 2 == 0) args.length / 2 - 1 else args.length / 2)
}

def sqrt(args : List[Double]) : List[Double] = { args.map(Math.sqrt(_)) }

def opMatch(arg : String) : (List[Double]) => Any =
{
arg match
{
case "sum" => return sum
case "product" => return product
case "median" => return median
case "sqrt" => return sqrt
_ => throw new java.lang.IllegalArgumentException("Unrecognized operator " + arg)
}
}
}
[/sourcecode]

Let's see... The translation of the REPL code into functions was very straightforward, although I have to admit to being somewhat disappointed that I could not use a type more abstract than Double. I don't know if I could define a trait and then apply it retroactively to types in the java.lang package.

opMatch() shows some functional goodness -- it's declared as a function that takes a String and return a function that takes a List of Doubles and returns Any kind of object.

I'm not sure if I could do something more precise for my needs, like "returns a Double or List[Double]". The pattern-matching in opMatch() is nice but not advanced.

If you have any observations to share, please comment below...