/* 22c22: Object Oriented Software Development Fall 2012 The University of Iowa Instructor: Cesare Tinelli */ /* Scala examples seen in class */ /* Named parameters and default values */ // unitWeight computes the unit weight of a body, given its weight and volume def unitWeight(weight:Double, volume:Double) = weight/volume // When calling unitWeight one must remember which argument is // the weight and which is the volume unitWeight(400,12) // Alternatively, one can specify explicitly which formal parameter // should get which input value unitWeight(weight = 400, volume = 12) // The notation is now order independent unitWeight(volume = 12, weight = 400) // The named parameter feature above with quite hand when used with // another one: it is possible to specify default values for input parameter def unitWeight(weight:Double, volume:Double = 10) = weight/volume // Then using named parameters it is possible to omit values for parameters // with a default. In that case the default value will be used unitWeight(weight = 400) /* Anonymous classes */ // It is possible to create instances of nameless (or anonymous) classes // by passing the body of such a class directly to the 'new' construct val o = new { val n = 1 var s = "Hi" def f(x:Int) = x + 1 } // The above defines an instance o of an anonymous class with // an immutable field n, a mutable field s, and a method f // Then o can be used as any other class instance o.n o.s o.s = "There" o.f // anonymous classes can be created as subclasses of other (named) classes // standard class definition class Person { private var f = 9 var name = "" var age = 0 var address = "" } // standard class instantiation val p = new Person // the expression below simultaneously defines an anononymous // subclass of Person and creates an instance of it val p1 = new Person { name = "Joe" age = 23 address = "Iowa City" } // p1 is an instance of an anonymous subclass of P that // resets the fields 'name' to "Joe", 'age' to 23 and // 'address' to "Iowa City" // As usual, the body of the anonymous subclass can access // all public or protected fields and methods of Person // Classes extended by an anonymous subclass can have // input parameters, as usual class Person (val name:String) { var age = 0 var address = "" def increaseAge(n: Int) = age = age + n } // p1 is an instance of an anonymous subclass of person // resets the fields 'age' to 23 and 'address' to "Iowa City" // This instance is created, the constructor of Person is used // to set the immutable field 'name' to "John Doe" val p1 = new Person("John" + " " + "Doe") { age = 23 address = "Iowa City" } // Note the initialization order: // first the actual parameter of Person (i.e., "John" + " " + "Doe") // is evaluated and 'name' is set to its value "John Doe"; // then then 'age' is set to 0 and 'address' to "", as specified in Person's body; // finally, 'age' is reset to 23 and 'address' to "Iowa City", as specified // in the anonymous subclass body // Note: like in all subclass definitions it is possible to add new fields // to the superclass or to override a superclass method val jane = new Person("Jane Doe") { age = 19 val street = "1234 Madison Ave" val city = "Coralville" address = street + ", " + city def incrementAge { age = age + 1 } override def increaseAge(n: Int) = { require (n > 0) age = age + n } } // jane has a 'street' and a 'city' field in addition to the Person fields // It also has a new method: incrementAge, and a new implementation of increaseAge // An alternative to anonymous classes is to use singleton ones // The underlying principle is the same (although, technically, // an object and an instance of an anonymous class are not the same thing). // In practice, using one or the other is mostly a matter of taste object jane extends Person("Jane Doe") { age = 19 val street = "1234 Madison Ave" val city = "Coralville" address = street + ", " + city def incrementAge { age = age + 1 } } /* Case classes */ // An empty tree is a tree // A node with an Int value and two subtrees is a tree // Nothing else is a tree abstract class Tree case object Empty extends Tree case class Node(value:Int, leftChild:Tree, rightChild: Tree) extends Tree val t = Empty val t1 = Node(5, Empty, Node(7,Empty, Empty)) val Node(n, _, _) = t1 val Node(n1, _, Node(n2, _, _)) = t1 abstract class MyList case class NonEmptyList(head:Int, tail: MyList) extends MyList case object EmptyList extends MyList val l = NonEmptyList(3, NonEmptyList(4, NonEmptyList(5,EmptyList))) val l1 = 3::(4::(5::Nil)) abstract class MyList[T] case class ::[T](head:T, tail: MyList[T]) extends MyList[T] case object Nil extends MyList[Nothing] abstract class Expr case class Var(name: String) extends Expr case class Number(num: Double) extends Expr case class UnOp(operator: String, arg: Expr) extends Expr case class BinOp(operator: String, left: Expr, right: Expr) extends Expr def evaluate(env:Map[String,Double], expr:Expr):Double = { expr match { case Number(n) => n case Var(s) => env(s) case UnOp("-", a) => -evaluate(env,a) case BinOp("*", l, r) => evaluate(env,l) * evaluate(env, r) case BinOp("+", l, r) => evaluate(env,l) + evaluate(env, r) } } // representing the expression -(x * (y + 3.5)) val e1 = new Var("x") val e2 = new Var("y") val e3 = new Number(3.5) val e4 = new BinOp("+", e2, e3) val e5 = new BinOp("*", e1, e4) val e6 = new UnOp("-", e5) val e = new UnOp("-", e5) abstract class Expr case class Var(name: String) extends Expr case class Number(num: Double) extends Expr case class UnOp(operator: String, arg: Expr) extends Expr case class BinOp(operator: String, left: Expr, right: Expr) extends Expr val e1 = Var("x") // -(x * (y + 3.5)) val e = UnOp("-", BinOp("*", Var("x"), BinOp("+", Var("y"), Number(3.5)))) e.operator e.arg val t = (1,2,3) val (x1,x2,x3) = t val UnOp(o, a) = e val BinOp(o2, l, r) = a def evaluate(env: Map[String,Double], expr:Expr): Double = expr match { case Var(v) => env(v) case Number(n) => n case UnOp("-", a) => -evaluate(env, a) case BinOp("+", l, r) => evaluate(env, l) + evaluate(env, l) case BinOp("*", l, r) => evaluate(env, l) * evaluate(env, l) } val env = Map("x" -> 2.0, "y" -> 1.0) evaluate(env, e) // Enumeration types object Color extends Enumeration { val Red = Value val Green = Value val Blue = Value } object Color extends Enumeration { val Red, Green, Blue = Value } object Direction extends Enumeration { val North, East, South, West = Value } // enumeration types can be used in pattern matching as usual val c = Color.Red c match { case Color.Red => 1 case _ => 2 } import Color._ // Alternative: case classes abstract class Color case object Red extends Color case object Green extends Color case object Blue extends Color val c = Red c match { case Red => 1 case _ => 2 }