Documente Academic
Documente Profesional
Documente Cultură
Otfried Cheong
February 8, 2011
1
In dynamically typed programming languages
like Python, the same name can be assigned to all /* This is C */
kinds of different objects: int m = 17;
float f = 17.0;
# This is Python!
m = 17 # int
m = "seventeen" # str Scala makes our life easier and our code shorter
m = 17.0 # float by using type inference. If Scala can detect what
the type of a variable name should be, then we do
This is flexible, and makes it easy to quickly write not need to indicate the type:
some code. It also makes it easy to make mistakes,
scala> var m : Int = 17 // ok
though. If you assign an object of the incorrect
m: Int = 17
type, you only find out during the execution of the
scala> var n = 18 // also ok!
program that something doesn’t work—you get a
n: Int = 18
runtime error.
scala> var f = 19.5
Scala is a statically typed language. This means
f: Double = 19.5
that a variable can only be assigned to objects of
scala> var h = "Hello World"
one fixed type, the type of the variable:
h: java.lang.String = Hello World
var m : Int = 17
m = 18 // ok Note how the interactive Scala interpreter tells you
m = "seventeen" // error! what the type of the name is even though we have
m = 18.0 // error! not indicated it at all. When you are not sure if it
is okay to omit the type of a name, you can always
The advantage of a statically typed language is write it.
that the compiler can catch type errors during the
compilation, before the program is even run. A
good compiler can also generate more efficient code
for a statically type language, as the type of objects
is already known during the compilation. Consider
the following Python function:
# This is Python!
def test(a, b):
print(a + b)
2
4 val variables and var variables is a new-line character, \t a tabulator, \’ and \"
are single and double quotes, and \\ is a backslash
Scala has two different kinds of variables: val vari-
character. (Inside triple-quote strings, the back-
ables and var variables. val stands for value, and
slash is not interpreted like this!)
a val variable can never change its value. Once
you have defined it, its value will always remain Sometimes we want to use integers larger than
the same: the maximal number allowed by the Int type. In
that case, we can use the type Long, which cov-
val m = 17 ers the range 2−63 to 263 − 1. To create a Long
m = 18 // error! object, write a number followed by L, like 123L.
(If you need even larger integers, you can use the
A var variable, on the other hand, can change its type BigInt, which represents arbitrarily large in-
value as often as you want: tegers. But BigInt is much slower.)
For completeness, the other basic types in Scala
var n = 17 are Byte, Short, and Float. Don’t use them unless
n = 18 // ok you know what you are doing.
You can convert objects between the basic types
So why are val variables useful? Because they by using their conversion methods:
make it easier to understand and to discuss a pro-
gram. When you see a val variable defined some- scala> 98.toChar
where in a large function, you know that this vari- res1: Char = b
able will always have exactly the same value. If the scala> ’c’.toDouble
programmer had used a var variable instead, you res2: Double = 99.0
would have to carefully go through the code to find scala> ’c’.toString
out if the value changes somewhere. res3: java.lang.String = c
It is considered good style in Scala to use val scala> 98.55.toInt
variables as much as possible. res4: Int = 98
3
• S.replace(c1, c2) returns a new string with early 1970s, and have somehow survived into mod-
all characters c1 replaced by c2; ern programming languages.) Like in Python, Java,
• S.replace(T1, T2) returns a new string with and C, these operators only evaluate their right
all occurrences of the substring T1 replaced hand operand when the result is not already known
by T2; from the value of the left hand operand.
• S.trim returns a copy of the string with white Sooner or later, you may meet the bitwise op-
space at both ends removed; erators: &, |, ^, ~, <<, >>, and >>>. They work
• S.split(T) splits the string into pieces and on the bit-representation of integers. (A common
returns an array with the pieces. T is a reg- mistake is to try to use ^ for exponentiation—it
ular expression (not explained here). To split does something completely different, namely a log-
around white space, use S.split("\\s+"). ical exclusive-or.) Don’t use these operators unless
For example: you know what you are doing.
The equality and inequality operators == and !=
scala> val S = "CS206 is nice" can be used for objects of any type, even to compare
scala> S.contains("ice") objects of different type.
res1: Boolean = true One of the great features of Scala is that you can
scala> S.indexOf("ice") define your own operators in a very flexible way. It
res2: Int = 10 is easy to abuse this feature, though, and we will
scala> S.indexOf("rain") not use it until later in the course.
res3: Int = -1 In Scala, the mathematical constants and func-
scala> S.replace(’i’, ’#’) tions are methods of the package math, the equiva-
res4: ... = CS206 #s n#ce lent of the Python math module. For instance:
scala> S.split("\\s+") scala> val pi = math.Pi
res5: ... = Array(CS206, is, nice) pi: Double = 3.141592653589793
scala> S.toLowerCase scala> val t = math.sin(pi/6)
res6: ... = cs206 is nice t: Double = 0.49999999999999994
scala> S.toUpperCase scala> math.sin(pi/4)
res7: ... = CS206 IS NICE res1: Double = 0.7071067811865475
scala> S.substring(5) scala> math.sqrt(2.0)/2
res8: ... = is nice res2: Double = 0.7071067811865476
scala> S.substring(5,8)
res9: ... = is If you don’t like the long function names, you can
scala> S.reverse import some of the functions:
res10: String = ecin si 602SC
scala> import math.sin
import math.sin
Also useful is the function format:
scala> sin(math.Pi/4)
scala> format("%5s %3d %-3d %g", res1: Double = 0.7071067811865475
"cs206", 12, 3, math.Pi)
res1: String = cs206 12 3 3.14159 Here, we imported only the sin-function. We could
also import several functions at once:
scala> import math.{Pi,sin,cos,sqrt}
7 Operators import math.{Pi, sin, cos, sqrt}
Scala’s numerical operators are +, -, /, and %. Like scala> sin(Pi/4)
in Python 2, Java, and C, the division operators / res1: Double = 0.7071067811865475
and % perform an integer division if both operands scala> sqrt(2.0)/2
are integer objects. Note that there is no power res2: Double = 0.7071067811865476
operator in Scala (you can use the math.pow library
function to do exponentiation). You can even import everything in the math-
package using this syntax:
You can use the shortcuts +=, -=, etc.
You can compare numbers or strings using ==, scala> import math._
!=, <, >, <=, and >=.
The boolean operators are ! for not, && for and, But note that unlike in Python, it is not neces-
and || for or, as in C and Java. (These some- sary to write an import-statement to use the math-
what strange operators were invented for C in the package!
4
8 Writing functions Since this case is common, Scala provides a special
shorthand notation for it:
Scala function definitions look very much like math-
ematical function definitions. For instance, in def greet(name : String) {
mathematics we would define a function f as fol- println("Hello " + name)
lows }
f : Z × Z → Z, f (a, b) = a + b
In Scala, this function would be written like this So we omit both the result type and the equals sign.
The parameters of a function are val variables,
scala> def f(a:Int, b:Int) : Int = a + b and so it is not allowed to change their value inside
f: (a: Int,b: Int)Int the function. This is different from Python, Java,
scala> f(3, 9) and C. So the following is illegal:
res1: Int = 12
def test(a: Int) {
a = a + 7 // illegal!
Note that we have indicated the type of each pa- println(a)
rameter of the function as well as the type of the }
result.
The body of the function is a block of statements.
A block can either be a single expression as in func- One interesting feature of Scala is that it sup-
tion f above, or it consists of several statements ports parameterless functions. Consider the differ-
surrounded by curly braces: ent syntax of functions f1 and f2 here:
def g(a: Int, b : Int) : Int = { scala> def f1() { println("Hi f1") }
val m = a*a - b*b f1: ()Unit
val s = a - b scala> def f2 { println("Hi f2") }
m/s f2: Unit
}
f1 is a normal function with an empty parameter
As mentioned above, indentation has no meaning to list, so you would normally call it as f2(). However,
the Scala compiler at all. Nevertheless, you should f2 is a parameterless function—you call it simply
indent your programs to be readable! as f2:
One big difference between Scala and Python, C,
scala> f1()
or Java is that no return statement is needed in
Hi f1
functions. The function automatically returns the
scala> f2
value of the last expression in the function body.
Hi f2
You can still use return statements when you want
to return from the function earlier.
You have to write the name of each parameter Note that Scala is somewhat generous and allows
type for a Scala function. The result type is ac- you to call f1 without the empty parentheses. How-
tually optional—you can try to omit it, and see if ever, calling f2 with parentheses is illegal:
the Scala compiler can figure it out. I recommend
scala> f1
always writing the result type until you have more
Hi f1
experience in Scala programming. Including the re-
scala> f2()
sult type also makes your program easier to read.
<console>:7: error: f2 of type Unit
Like in Python, every Scala function returns a
does not take parameters
value. A function that does not need to return any-
thing will return the special value (), the only ob-
ject of the special type Unit, similar to the special We will see later why this is actually a very useful
value None in Python. feature.
So a function that returns nothing useful could
be written like this:
5
9 Conditional expressions
for (i <- 1 until 10)
The syntax of the conditional expression is
println(i + "\t: " + i*i)
if (pts > 80) {
s = "well done!" Here i until j includes the integers {i, i +
} else { 1, . . . , j − 1}. Note that curly braces are not needed
s = "practice harder" since there is only one statement inside the loop.
} Alternatively, you could have written
The then-part and the else-part are blocks, that is, 11 Arrays
either a single statement or a sequence of state-
ments surrounded by braces. So the example above To store many objects of the same type, we can use
can be abbreviated like this: an array:
Scala does not have break and continue state- scala> val A = new Array[Int](100)
ments. (You may understand why later in the A: Array[Int] = Array(0, ... 0)
course.)
The simple while loop above could have been Here, Array[Int] indicates the type of object you
written more easily using a for loop as follows: want to create, and 100 is the number of elements.
6
When you create an array of numbers, all elements
are initially zero. It’s different for other arrays: scala> for (i <- L)
| println(i)
scala> val B = new Array[String](5) CS206B
B: Array[String] = is
Array(null, null, null, null, null) the
best
Here, the elements of the new array have the spe-
cial value null, similar to None in Python. This This is called “iterating over an array” and is quite
value typically means that a variable has not been similar to the for-loop in Python.
initialized yet. Except for the basic numeric types,
a variable of any type can have the value null.
Here is a short list of the most useful methods of 12 Command line arguments
arrays: When you call a Scala script from the command
• A.contains(x) tests if array contains an ele- line, you can add command line arguments:
ment equal to x;
• A.take(n) returns a new array that has the scala script.scala I love CS206!
first n elements of A;
• A.drop(n) returns a new array that has the The command line arguments are available inside
elements of A except for the first n elements; the script in the array args. If script.scala looks
• A.max, A.min, A.sum return the largest, small- like this:
est, and sum of elements in the array;
• A.reverse returns a new array with the ele- for (s <- args)
ments of A in reverse order; println(s)
• A.sorted returns a new Array with the ele-
ments of A in sorted order; then the output of the command line call above
• A.mkString returns a string with all elements would be this:
of A concatenated together;
$ scala script.scala I love CS206!
• A.mkString(sep) returns a string with all el-
I
ements of A concatenated, using sep as a sep-
love
arator;
CS206!
• A.mkString(start, sep, end) returns a
string with all elements of A concatenated,
using sep as a separator, prefixed by start Here is another example script, triangle.scala:
and ended by end.
val n = args(0).toInt
For example:
7
def sieve(n : Int) { scala> val (x,y) = a
val sqrtn = math.sqrt(n).toInt x: Int = 3
val S = new Array[Boolean](n) y: Double = 5.0
for (i <- 2 until n)
S(i) = true The parentheses around x and y are required—if
for (i <- 2 until sqrtn) { you omit them, something completely different hap-
if (S(i)) { pens (try it)!
// i is a prime number
// remove all multiples of i
var j = i * i 14 Reading from the terminal
while (j < n) { You already noticed the functions print, println,
S(j) = false and printf for printing to the terminal.
j += i To read input from the terminal, there are a num-
} ber of read-methods:
} • readLine() reads a line and returns it as a
} string (without the new-line character at the
for (i <- 2 until n) { end);
if (S(i)) { • readLine(prompt) prints a prompt and then
print(i); print(" ") reads a line as above, as in this example:
}
} val s = readLine("What do you say? ")
println() println("You said: ’" + s + "’")
}
• readInt() reads a line and returns it as an
val n = args(0).toInt integer;
sieve(n) • readDouble() reads a line and returns a float-
ing point number;
• readBoolean() reads a line and returns a
Boolean ( “yes”, “y”, “true”, and “t” for true,
13 Tuples and anything else for false); and
Like Python, Scala supports tuples: • readChar() reads a line and returns the first
character.
scala> var a = (3, 5.0) For instance:
a: (Int, Double) = (3,5.0)
print("How many beer? ")
val n = readInt()
Unlike in Python, the parentheses are stricly nec- printf("You ordered %d beer\n", n)
essary. Note the type: the first element of tuple a print("Are you sure? ")
is an Int, the second one a Double. You can assign if (readBoolean())
only tuples of the correct type to the variable a: printf("Serving %d beer\n", n)
scala> a = (2, 7)
a: (Int, Double) = (2,7.0) All the print and read methods mentioned
scala> a = (2.0, 7) above are actually methods of the Console object,
<console>:5: error: type mismatch; but Scala makes them available as if they were
found : Double(2.0) global functions because they are so common.
required: Int
15 Reading and writing files
You can access the elements of a tuple as To read a text file, create a scala.io.Source ob-
fields _1, _2, and so on: ject like this:
scala> a._1 val F = scala.io.Source.fromFile(fname)
res1: Int = 3
Or, if you think the class name is too long, import
You can also “unpack” a tuple like in Python: the class first:
8
import scala.io.Source val food = args(0)
val F = Source.fromFile(fname)
val banchan = food match {
case "rice" => "kimchi"
The most important method of the Source object F case "noodles" => "gakdugi"
is getLines, which allows you to iterate over all the case "pizza" => "pickles"
lines of the file: case "bread" => "cheese"
case _ => "huh?"
val fname = args(0) }
val F = scala.io.Source.fromFile(fname) printf("With %s, we recommend %s\n",
for (line <- F.getLines()) food, banchan)
printf("%4d %s\n", line.length, line)
F.close() Note that there is no break statement: A case ends
when the next case starts. The match expression
really is an expression and returns a value: in this
The lines returned in the line variable contain no case, we assign it to a variable.
new-line characters anymore. The default case is indicated by an underscore _.
You should call F.close() when you are done
with the Source.
import java.io.PrintStream
val S = new PrintStream("test.txt")
Console.withOut(S) {
for (i <- 1 to 10)
printf("%3d --> %d\n", i, i*i)
}
S.close()
16 Selecting alternatives
The match expression is similar to the switch-
statement in Java and C, but really much more
powerful, as we will see later. For the moment,
you can use it to conditionally execute different ex-
pressions depending on the value of some object: