class Point(val x: Double, val y: Double) { def move(dx: Double, dy: Double) = new Point(x + dx, y + dy) def distanceFromOrigin = math.sqrt(x * x + y * y) override def toString = "(" + x + ", " + y + ")" }
val myFirstPoint = new Point(3, 4)
x
, y
, move
, distanceFromOrigin
, toString
override
when overriding methods (here Object.toString
)()
for parameterless accessor methodsval
parameter gives rise to instance variable and accessor
Point(val x, val y)
val
gives rise to instance variable without accessor.
class Rand(gen: java.util.Random) { // No val
def value = gen.nextInt()
}
class Rand { private val gen = new java.util.Random(42) def value = gen.nextInt() }
class LabeledPoint(x: Double, y: Double, val label: String) extends Point(x, y)
object
= class with only one instanceApp
trait (similar to an interface) for an application object:
object Main extends App { println("My first Scala App") }
static
methods in Java. Examples:
List.tabulate Source.fromURL
math.ceil
is not defined in an object—it's in the scala.math
packagecase class ClassName(field1 : Type1, field2 : Type2, ...) extends Superclass
abstract class SimpleTree case class Leaf(value : Int) extends SimpleTree case class Node(left : SimpleTree, right : SimpleTree) extends SimpleTree
ClassName(arg1, arg2, ...)
Node(Node(Leaf(3), Leaf(2)), Node(Leaf(7), Node(Leaf(6), Leaf(8))))
selectorExpr match { case pattern => expr ... case pattern => expr }
tree match { case Node(l, r) => expr1 case Leaf(v) => expr2 }
l
, r
, v
are bound to the values in the case class instances; you can use them in the expressions.
def sum(t : SimpleTree) : Int = t match { case Node(l, r) => sum(l) + sum(r) case Leaf(v) => v }
case _
3 + 4 * x
class Expr case class Number(value : Int) extends Expr case class Variable(name : String) extends Expr case class Operator(left : Expr, right : Expr, f: (Int, Int) => Int) extends Expr
Operator(Number(3), Operator(Number(4), Variable("x"), _ * _), _ + _)
3 + 4 * x
into Scala valueMap[String, Int]
Map(key1 -> value1, key2 -> value2, ...)
yields map with given key/value pairsmap + (key -> value)
yields map with new key/value pairmap(key)
yields value of key (must exist)map.get(key)
yields Option
, either Some(value)
or None
def eval(expr : Expr, symbols : Map[String, Int]) : Int = expr match { case Number(num) => num case Variable(name) => symbols(name) case Operator(left, right, f) => f(eval(left, symbols), eval(right, symbols)) }
val result = sum(1, 7, 2, 9) val result2 = sum(3, 1, 4, 1, 5, 9, 2, 6)
Int*
def sum(args: Int*) = args.reduce(_ + _)
Seq[Int]
Seq[Int]
, you cannot pass it directly.: _*
ascription:
val s = sum(1 to 5: _*) // Consider 1 to 5 as an argument sequence
def recursiveSum(args: Int*) : Int = { if (args.length == 0) 0 else args.head + recursiveSum(args.tail : _*) }
lab7/report.txt
inside the Git repo. Include the coder's name in the report! Add the classes Expr
, Number
, Variable
, Operator
to a Main.scala file, inside a Main
object. Add the eval
method to the Main
object. In the body of the Main
object, construct the tree
print it and and evaluate it with a symbol table in which x
is 5. What is the code of your Main.scala file?
Now we want to process variable definitions (which we will write as val x = expr
in the next lecture). For now, we will assume that they are already parsed into instances of
case class Definition(name : String, expr : Expr)
For example, a definition val x = 2
would be a Definition("x", Number(2))
. A definition changes the symbol table. In a functional setting, that means we need to return a new table that contains all bindings in the old table and the new binding. For example,
val def1 = Definition("x", Number(2)) val def2 = Definition("y", Variable("x")) val sym0 = Map[String, Int]() val sym1 = eval(def1, sym0) // "x" -> 2 val sym2 = eval(def2, sym1) // "x" -> 2, "y" -> 2 System.out.println(sym2)
Implement this eval
method. What is the code of your method? (You can get the fields of a definition as defi.name
, defi.expr
.)
Note: This eval
method requires you to call the eval
method that we defined for expressions, but it is a different method—it consumes a definition and a symbol table, yielding another symbol table.