CS 334: CS 334 Scala Notes

Basic Operations in Interpreter

scala> 3+5; 
res0: Int = 8

scala> res0 * 2;
res1: Int = 16

// val name are immutable:
scala> val six = 6;
six: Int = 6

scala> six = 7;
error: reassignment to val
       six = 7;
           ^

// var names are mutable:
scala> var n = 1;
n: Int = 1

scala> n = 2;
n: Int = 2

Method Definitions and Types

// function definition:
scala> def succ(x: Int) = x + 1;
succ: (x: Int)Int

scala> succ(12);
res2: Int = 13

// lambda function:
scala> val succ = (x : Int) => x + 1;
succ: (Int) => Int = <function1>

scala> succ(3);
res3: Int = 4

// recursive function:
scala> def fact(n:Int): Int = if (n == 0) 1 else n * fact(n-1);
fact: (n: Int)Int

Basic Values and Types.

scala> true;
res4: Boolean = true

scala> false;
res5: Boolean = false

scala> 3;
res6: Int = 3

scala> 43.3;
res7: Double = 43.3

// most types fully compatible with Java:
scala> "moo";
res8: java.lang.String = moo

scala> val str = "cow";
str: java.lang.String = cow

scala> str.length();
res9: Int = 3

scala> str.toUpperCase();
res10: java.lang.String = COW

// Abbreviates in method calls with no arguments:
scala> str.toUpperCase;
res11: java.lang.String = COW

scala> str toUpperCase;
res12: java.lang.String = COW

Tuples

scala> val tuple = (1,"moo");
tuple: (Int, java.lang.String) = (1,moo)

scala> val (x,y) = tuple;
x: Int = 1
y: java.lang.String = moo

Loops

scala> var n = 0;
n: Int = 0

scala> while (n < 3) { println(n); n = n + 1; }
0
1
2

scala> for (n <- 1 to 3) { println(n); }
1
2
3

More on For loops later...

Lists

scala> val list = List[Int](1,2,3);
list: List[Int] = List(1, 2, 3)

scala> val strList = List[String]("1","2","3");
strList: List[String] = List(1, 2, 3)

scala> val empty = Nil;
empty: scala.collection.immutable.Nil.type = List()

// cons and append operations
scala> 2 :: list 
res15: List[Int] = List(2, 1, 2, 3)

scala> list ::: list 
res16: List[Int] = List(1, 2, 3, 1, 2, 3)

// a polymorphic function (with matching)
scala> def build[T](t : T, n : Int) : List[T] =
        n match {
          case 0 => Nil
          case v => t :: build(t, v-1);
        }
build: [T](t: T,n: Int)List[T]

scala> build("cow",3);  
res17: List[java.lang.String] = List(cow, cow, cow)

// Can iterate over lists with for loop  (Iteratables)
scala> for (l <- list) println(l);
1
2
3

Lists are Objects Too

scala> val list = List("A","B","C");
list: List[java.lang.String] = List(A, B, C)

scala> list.head;
res19: java.lang.String = A

scala> list.length;
res20: Int = 3

scala> list(2);
res21: java.lang.String = C

scala> list.foreach(x => println(x));          
A
B
C

scala> list.foreach(println(_));     
A
B
C

scala> for (l <- 1 to 3) println(l);
1
2
3

scala> 1 to 3;
res26: scala.collection.immutable.Range.Inclusive ... = Range(1, 2, 3)

scala> (1 to 3).foreach(println(_));
1
2
3

scala> for (c <- "moo") println(c.toUpper);
M
O
O

// The usual map / filter / etc operations
scala> val list = List(11,21,31);
list: List[Int] = List(11, 21, 31)

scala> list.map( (x : Int) => "#" + x.toString);
res29: List[java.lang.String] = List(#11, #21, #31)

scala> list.filter(_<=21);
res30: List[Int] = List(11, 21)


// foldLeft is a CURRIED method
scala> list.foldLeft (0) ( (result,x) => result + x ); 
res31: Int = 63

scala> list.foldLeft (0) (_ + _)
res32: Int = 63

scala> list.foldLeft ("") ( (result,x) => result + " #" + x);       
res33: java.lang.String =  #11 #21 #31

scala> list.foldLeft ("") ( _ + " #" + _)
res34: java.lang.String =  #11 #21 #31

Mutable Lists

// mutation via mutable name (but immutable lists...)
scala> var list2 = List(1,2,3);
list2: List[Int] = List(1, 2, 3)

scala> list2 = 2 :: list2;
list2: List[Int] = List(2, 1, 2, 3)

scala> list2 = list2 ::: list2;
list2: List[Int] = List(2, 1, 2, 3, 2, 1, 2, 3)

// mutable List structure
scala> import scala.collection.mutable.MutableList;
scala> val mlist = new MutableList[Int]();
mlist: scala.collection.mutable.MutableList[Int] = MutableList()

scala> mlist += 3;
res38: mlist.type = MutableList(3)

scala> mlist ++= mlist;
res39: mlist.type = MutableList(3, 3, 3)

scala> mlist ++= mlist;
res40: mlist.type = MutableList(3, 3, 3, 3, 3, 3, 3)

scala> mlist.foreach(println(_));
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3

scala> (0 /: mlist) (_+_);
res43: Int = 45

Maps

scala> var romans = Map(1 -> "I", 2 -> "II", 3 -> "III", 4 -> "IV")
romans: scala.collection.immutable.Map[Int,java.lang.String] = 
    Map((1,I), (2,II), (3,III), (4,IV))

scala> romans(4);
res44: java.lang.String = IV

scala> romans(6);
java.util.NoSuchElementException: key not found: 6
    at scala.collection.MapLike$class.default(MapLike.scala:223)
    at scala.collection.immutable.HashMap.default(HashMap.scala:35)
        ...

scala> romans += (6 -> "VI");

scala> romans(6);
res47: java.lang.String = VI

Point Class

//Mutable fields x and y, accessor methods, overriding a method
class Point {
  var x = 0;
  var y = 0;
  def getX() = x;
  def getY() = y;
  override def toString() = {
    "(" + x + "," + y + ")";
  }
}

object PointMain {
  def main(args : Array[String]) = {
    val p = new Point();
    println(p.toString());
    p.x = 3;
    println(p.toString());
  }
}



// Constructor values, immutable x, mutable y
class Point(val x : Int, var y : Int) {
  def getX() = x;
  def getY() = y;
  override def toString() = {
    "(" + x + "," + y + ")";
  }
}

object PointMain {
  def main(args : Array[String]) = {
    val p = new Point(1,0);
    println(p.toString());  // prints (1,0)
    p.y = 2;
    println(p.toString());  // prints (1,2)
  }
}

// And ColorPoints...
class ColorPoint(val color : String, x : Int, y : Int) 
      extends Point(x,y) {
  override def toString() = {
    super.toString() + " -- " + color;
  }
}


object PointMain {
  def main(args : Array[String]) = {
    val p = new ColorPoint("red",0,0);
    p.y = 2;
    println(p.toString());
  }
}

MultiSet and Generic Classes

class MultiSet[T] {
    var elems = List[T]();
    def add(t : T) : Unit = {
        elems = t::elems;
    }

    def addAll(ts : List[T]) : Unit = {
        elems = elems:::ts;
    }

    def count(t : T) : Int = {
    elems.count(_ == t);
    }
}

object MultiSet {
    def main(args : Array[String]) = {

    val s = new MultiSet[String]();
    s.add("cow");
    s.add("Moo");
    s.add("cow");
    s.add("cow");
    s.add("cow");
    println(s.count("cow"));

    val s2 = new MultiSet[Int]();
    s2.addAll(List(1,2,3,2,1,2,3,3));
    println(s2.count(3));

    }
}

Operator Overloading

class Rational(x: Int, y: Int = 1) {

  private def gcd(a: Int, b: Int): Int = {
    if (b == 0) a else gcd(b, a % b);
  }

  private val g = gcd(x, y);

  val numer = x / g;
  val denom = y / g;

  def +(that: Rational) =
    new Rational(numer * that.denom + that.numer * denom,
         denom * that.denom);

  def -(that: Rational) =
    new Rational(numer * that.denom - that.numer * denom,
         denom * that.denom);

  def *(that: Rational) =
    new Rational(numer * that.numer, denom * that.denom);

  def /(that: Rational) =
    new Rational(numer * that.denom, denom * that.numer);

  override def toString = numer + "/" + denom;
}

object Rational {
  def main(args : Array[String]) = {
    val a = new Rational(2, 4)
    val b = new Rational(3)

    println(a + " + " + b + " = " + (a + b))
    println(a + " - " + b + " = " + (a - b))
    println(a + " * " + b + " = " + (a * b))
    println(a + " / " + b + " = " + (a / b))
  }
}

Case Classes

/*

An example of case classes for Expresssion Trees.  We use pattern
matching to convert a tree to a String, and standard method
inheritance and virtual dispatch to "flip" an expression by negating
numbers and changing oeprators.

*/

// sealed means no subclasses outside of this file.
sealed abstract class Expr {
  def flip() : Expr  // abstract method
}

case class Constant(x: Double) extends Expr {
  override def flip() = Constant(-x)
}

case class Sum(l: Expr, r: Expr) extends Expr {
  override def flip() = Product(l.flip(), r.flip())
}

case class Product(l: Expr, r: Expr) extends Expr {
  override def flip() = Sum(l.flip(), r.flip())
}


object Expr {
  def makeString(exp: Expr): String = {
    exp match {
      case Constant(x) => x.toString
      case Sum(l, r) 
             => "(" + makeString(l) + " + " + makeString(r) + ")"
      case Product(l, r) 
             => "(" + makeString(l) + " * " + makeString(r) + ")"
    }
  }

  def main(args : Array[String]) {
    val e = Product(Sum(Constant(1), Constant(4)), Constant(6))
    println(makeString(e.flip()))
  }
}

Another Example

class Color(val red:Int, val green:Int, val blue:Int) {
  def negate() = new Color(255 - red, 255 - green, 255 - blue);
}

case class Red(r:Int) extends Color(r, 0, 0);
case class Green(g:Int) extends Color(0, g, 0);
case class Blue(b:Int) extends Color(0, 0, b);

object Color {
  def printColor(c:Color) = {
    c match {
      case Red(v) => println("Red: " + v)
      case Green(v) => println("Green: " + v)
      case Blue(v) => println("Blue: " + v)

      case col:Color => {
    print("(" + col.red + ",")
    print(col.green + ",")
    println(col.blue + ")")
      }
    }
  }

  def main(args : Array[String]) {
    printColor(Red(100));
    printColor(Blue(220));
    printColor(Blue(220).negate);

    printColor(new Color(100, 200, 50));
  }
}

Option types

scala> val result = romanNumeral.get(11);
result: Option[java.lang.String] = None

// See Def of Map

scala> result match {
         case None => println("Not Found");
         case Some(v) => println(v);
       }
Not Found


// Option implementation
sealed abstract class Option[+A] ... {
  def isEmpty: Boolean
  def get: A
}

case class Some[+A](x: A) extends Option[A] {
  def isEmpty = false
  def get = x
}

case object None extends Option[Nothing] {
  def isEmpty = true
  def get = throw new NoSuchElementException("None.get")
}

Functions Are Objects

Look at API

  • Function Class
  • Partial Function
  • Array
  • Map

  • Map's apply method gets called when romanNumberal(3) is evaluation

  • Can define apply method on any class, with any argument types...

Design Patterns

  • Iterator
  • Singleton
  • Subject-Observer