Instructor: James Riely
false || true
1 + 2
("hello" + " " + "world").length
val dir = java.io.File ("/tmp")
dir.listFiles.filter (f => f.isDirectory && f.getName.startsWith ("c"))
5:Int
is an object with methods
5.toDouble
scala.Int
)
5.+ (6)
scala.runtime.RichInt
adds more methods
5.max (6)
e1.f(e2)
can be written as e1 f e2
5 + 6
5 max 6
def f () = 5 - "hello" // rejected by type checker
java.lang.Object
to scala.AnyRef
int x = 10; // declare and initialize x
x = 11; // assignment to x OK
int x = 10; // declare and initialize x
x = 11; // assignment to x OK
var x = 10 // declare and initialize x
x = 11 // assignment to x OK
final int x = 10; // declare and initialize x
x = 11; // assignment to x fails
Final.java:4: error: cannot assign a value to final variable x
const int x = 10; // declare and initialize x
x = 11; // assignment to x fails
final.c:6:3: error: assignment of read-only variable ‘x’
val x = 10 // declare and initialize x
x = 11 // assignment to x fails
final.scala:3: error: reassignment to val
(e_1, e_2, ..., e_n)
(begin e_1 e_2 ... e_n)
{e_1; e_2; ...; e_n}
{
e_1
e_2
...
e_n
}
def plus (x:Int, y:Int) : Int = x + y
def fact (n:Int) : Int = if n <= 1 then 1 else n * fact (n - 1)
def fact (n:Int) : Int =
println ("called with n = %d".format (n))
if n <= 1 then
println ("no recursive call")
1
else
println ("making recursive call")
n * fact (n - 1)
def
can be used as non-parameterized methods
val
and def
differ in when initializer is executed.
val
is strict; def
is non-strict
scala> class C:
val x = 1
def y = 1
scala> :javap -p -c -filter C
public class C {
private final int x;
public int x();
1: getfield #18 // Field x:I
public int y();
0: iconst_1
public C();
10: putfield #18 // Field x:I
}
scala> class C:
val x = 1
var z = 1
scala> :javap -p -c -filter C
public class C {
private final int x;
private int z;
public int x();
1: getfield #19 // Field x:I
public int z();
1: getfield #23 // Field z:I
public void z_$eq(int);
2: putfield #23 // Field z:I
public C();
6: putfield #19 // Field x:I
11: putfield #23 // Field z:I
}
scala.collection
scala.collection.immutable
scala.collection.mutable
java.util
is available
Array[Int]
final List<Integer> xs = new List<> ();
xs.add (4); xs.add (5); xs.add (6); // mutating list OK
xs = new List<> (); // reassignment fails
var
variable
var xs = List (4, 5, 6)
xs = 0 :: xs // reassignment OK
xs (1) = 7 // mutating list fails
val p : (Int, String) = (5, "hello")
val x : Int = p(0)
public class Pair<X,Y> {
final X x;
final Y y;
public Pair (X x, Y y) { this.x = x; this.y = y; }
static void f () {
Pair<Integer, String> p = new Pair<Integer, String> (5, "hello");
Pair<Integer, String> q = new Pair<> (5, "hello"); // infer type params
int x = p.x;
}
}
def a(p:(Int,Int)) = p match
case (x,y) => x+y
...branches and binds pattern variables
def b(p:(Int,Int)) =
if p==null then throw MatchError(p)
val x = p(0)
val y = p(1)
x + y
::
is an infix cons
operator
(define xs (cons 11 (cons 21 (cons 31 (cons 41 ())))))
val xs = 11 :: (21 :: (31 :: (41 :: Nil)))
val xs = 11 :: 21 :: 31 :: 41 :: Nil // right associative
(list 1 2 (+ 1 2))
List (1, 2, 1 + 2)
(car xs)
(cdr xs)
xs.head
xs.tail
def f(xs: List[Int]) = xs match
case Nil => "List is empty"
case y::ys => "List is non-empty, head is %d".format (y)
...branches and binds pattern variables
def g(xs: List[Int]) =
if xs == Nil then "List is empty"
else if xs.isInstanceOf[::[Int]] then
val zs = xs.asInstanceOf[::[Int]]
val y : Int = zs.head
val ys : List[Int] = zs.tail
"List is non-empty, head is %d".format (y)
else throw MatchError(xs)
def f (xs: List[(Int,String)]) = xs match
case Nil => "List is empty"
case _::Nil => "List has one element"
case _::(x,_)::_ => s"The second int is ${x}"
val zs = List ((11,"dog"), (21,"cat"), (31,"pig"))
f(zs)
_
means don't care
def f (xs: List[(Int,String)]) =
if xs == Nil then "List is empty"
else if xs.tail == Nil then "List has one element"
else s"The second int is ${xs.tail.head(0)}"
val zs = List ((11,"dog"), (21,"cat"), (31,"pig"))
f(zs)
isEmpty
, head
, tail
by pattern matching
def isEmpty (xs:List[Int]) : Boolean = xs match
case Nil => true
case y::ys => false
def head (xs:List[Int]) : Int = xs match
case Nil => throw NoSuchElementException ()
case y::ys => y
def tail (xs:List[Int]) : List[Int] = xs match
case Nil => throw NoSuchElementException ()
case y::ys => ys
head
method from List
class
List (1, 2, 3).head
head
method defined on previous slide
head (List (1, 2, 3))
def length (xs:List[Int]) : Int = xs match
case Nil => 0
case y::ys => 1 + length (ys)
def length [X] (xs:List[X]) : Int = xs match
case Nil => 0
case y::ys => 1 + length (ys)
_
def length [X] (xs:List[X]) : Int = xs match
case Nil => 0
case _::ys => 1 + length (ys)
length (List (1, 2, 3))
--> length (1::(2::(3::Nil)))
--> 1 + length (2::(3::Nil)) // y = 1, ys = 2::(3::Nil)
--> 1 + (1 + length (3::Nil)) // y = 2, ys = 3::Nil
--> 1 + (1 + (1 + length (Nil))) // y = 3, ys = Nil
--> 1 + (1 + (1 + 0))
--> 1 + (1 + 1)
--> 1 + 2
--> 3
def length (xs:List[Int]) : Int = xs match
case Nil => 0
case y::ys => 1 + length (ys)
append (1::(2::Nil), 3::Nil)
--> 1::(append (2::Nil, 3::Nil)) // z = 1, zs = 2::Nil
--> 1::(2::(append (Nil, 3::Nil))) // z = 2, zs = Nil
--> 1::(2::(3::Nil)) // z = 2, zs = Nil
def append [X] (xs:List[X], ys:List[X]) : List[X] = xs match
case Nil => ys
case z::zs => z::(append (zs, ys))
1
and 2
in head
(3::Nil)
is reused (shared)
append
def append [X] (xs:List[X], ys:List[X]) : List[X] = xs match
case Nil => ys
case z::zs => z::(append (zs, ys))
List
class has builtin method :::
scala> ((1 to 5).toList) ::: ((10 to 15).toList)
res1: List[Int] = List(1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15)
def f [X] (xs:List[X]) : List[X] = xs match
case Nil => Nil
case y::ys => f (ys) ::: List (y)
f
do?
f (Nil)
--> Nil
f (3::Nil)
--> f (Nil) ::: List (3)
--> Nil ::: List (3)
--> List (3)
f (2::(3::Nil))
--> f (3::Nil) ::: List (2)
--> List (3) ::: List (2)
--> List (3, 2)
f (1::(2::(3::Nil)))
--> f (2::(3::Nil)) ::: List (1)
--> List (3, 2) ::: List (1)
--> List (3, 2, 1)
f
is reverse