Lab 5. Data types in Scala
Objectives:
- get familiar with algebraic data types
- get familiar with pattern matching and recursion with them
5.1 Natural Numbers
Given the following implementation of the natural numbers, solve the next few exercises.
trait Nat case object Zero extends Nat case class Succ(x: Nat) extends Nat
5.1.1 Write a function which takes two natural numbers, and returns their sum.
def add(x: Nat, y: Nat): Nat = ???
5.1.2 Write a function which takes two natural numbers, and returns their product.
def multiply(x: Nat, y: Nat): Nat = ???
5.1.3 Write a function which takes an int and converts it to a Nat.
def toNat(x: Int): Nat = ???
5.2 Option
Option = carrier (like a box or a container) for a single or no element, of a given type. (Ex. Some(_)
or None
)
We use Option to write robust functions, in case they return null or fail to return an accepted value.
5.2.1 Let's revisit the function realtrycatch
now that we have a type that represents the possibility of error. If an error occurs (try function returns None
), the catch function will be called instead.
def realrealtrycatch(t: => Option[Int], c: => Int): Int = { ??? }
(!) 5.2.2 Refactor the function toNat(), so that it takes an integer (a positive or negative number) and returns a “container” of a Nat.
def toNatOpt(x: Int): Option[Nat] = ???
(!) 5.2.3 Refactor the function add(), so that it takes two “containers” of Nats and returns a “container” of a Nat.
def addOpt(x: Option[Nat], y: Option[Nat]): Option[Nat] = ???
5.3 Binary Trees
Given the implementation of binary trees from the previous lab:
trait BTree case object TVoid extends BTree case class Node(left: BTree, info: Int, right: BTree) extends BTree def leaf_node(value : Int) : BTree = { Node(TVoid, value, TVoid) } object BTreePrinter { case class PrintInfo(len: Int, center: Int, text: List[String]) def pp(tree: BTree): PrintInfo = tree match { case TVoid => PrintInfo(3, 2, List("Nil")) case Node(left, value, right) => val strValue = value.toString val ppL = pp(left) val ppR = pp(right) val nlen = ppL.len + ppR.len + 1 val ncenter = ppL.len + 1 require(strValue.length <= nlen, "Nice try") val alignedX = " " * (ncenter - (strValue.length / 2) - 1) + strValue val centerLine = " " * (ncenter - 1) + "|" val dottedLine = " " * (ppL.center - 1) + "-" * (nlen - ppL.center - (ppR.len - ppR.center + 1) + 2) val downLines = " " * (ppL.center - 1) + "|" + " " * (nlen - ppL.center - (ppR.len - ppR.center +1)) + "|" val combinedLines = zipPad("",(l, r) => l ++ (" " * (ppL.len - l.size + 1 )) ++ r, ppL.text, ppR.text) PrintInfo(nlen, ncenter, alignedX :: centerLine :: dottedLine :: downLines :: combinedLines) } def zipPad[A](pad: A, f: (A, A) => A, left: List[A], right: List[A]): List[A] = (left, right) match { case (Nil, Nil) => Nil case (x :: xs, Nil) => x :: zipPad(pad, f, xs, List(pad)) case (Nil, y :: ys) => f(pad, y) :: zipPad(pad, f, List(pad), ys) case (x :: xs, y :: ys) => f(x, y) :: zipPad(pad, f, xs, ys) } def printTree(tree: BTree): String = "\n" + pp(tree).text.mkString("\n") } extension(t: BTree) { def toStringTree: String = BTreePrinter.printTree(t) }
And arborică:
val arborica = Node(Node(leaf_node(-1), 5, TVoid), 1, Node(leaf_node(4), 2, Node(leaf_node(3), 6, leaf_node(7)))) arborica.toStringTree
Solve the next few exercises:
5.3.1 Write a function which takes a BinaryTree and returns its depth.
def depth(tree: BTree): Int = ???
5.3.2 Write a function which takes a BinaryTree and returns the number of nodes in its subtree.
def subtree(tree: BTree): Int = ???
5.3.3 Write a function which takes a BinaryTree and returns the number of nodes with an even number of children.
def evenChildCount(tree: BTree): Int = ???
5.3.4 Write a function which takes a BinaryTree and returns the number of nodes whose values follow a certain rule.
def countNodes(tree: BTree, cond: Int => Boolean): Int = ???
(!) 5.3.5 Write a function which takes two BinaryTree and tries to assign the second tree as a child of the first. It should return a “container” of a BinaryTree .
def append(tree1: BTree, tree2: BTree): Option[BTree] = ???
5.4 Expression evaluation
Given the following implementation of expressions, solve the next few exercises.
trait Expr case class Atom(a: Int) extends Expr case class Add(e1: Expr, e2: Expr) extends Expr case class Mult(e1: Expr, e2: Expr) extends Expr
5.4.1 Write a function which takes an Expression and evaluates it.
def evaluate(e: Expr): Int = ???
5.4.2 Write a function which takes an Expression and simplifies it. (Ex. a * (b + c) → remove parentheses → ab + ac)
def simplify(e: Expr): Expr = ???
5.4.3 Write a function which takes an Expression and removes 'useless' operations. (Ex. a * 1 → a, a + 0 → a)
def optimize(e: Expr): Expr = ???
5.5 Matrix manipulation
We shall represent matrices as lists of lists, i.e. values of type [ [Integer ] ]
. Each element in the outer list represents a line of the matrix.
Hence, the matrix
$ \displaystyle \left(\begin{array}{ccc} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \\ \end{array}\right)$
will be represented by the list [ [1,2,3],[4,5,6],[7,8,9] ]
.
To make signatures more legible, add the type alias to your code:
type Matrix = List[List[Int]]
which makes the type-name Matrix
stand for [ [Integer] ]
.
5.5.1 Write a function that computes the scalar product with an integer:
$ \displaystyle 2 * \left(\begin{array}{ccc} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \\ \end{array}\right) = \left(\begin{array}{ccc} 2 & 4 & 6 \\ 8 & 10 & 12 \\ 14 & 16 & 18 \\ \end{array}\right)$
def scalarProd(m: Matrix)(v: Int): Matrix = ???
5.5.2 Write a function which adjoins two matrices by extending rows (horizontally):
$ \displaystyle \left(\begin{array}{cc} 1 & 2 \\ 3 & 4\\\end{array}\right) hjoin \left(\begin{array}{cc} 5 & 6 \\ 7 & 8\\\end{array}\right) = \left(\begin{array}{cc} 1 & 2 & 5 & 6 \\ 3 & 4 & 7 & 8\\\end{array}\right) $
def hJoin(m1: Matrix, m2: Matrix): Matrix = ???
5.5.3 Write a function which adjoins two matrices by adding new rows (vertically):
$ \displaystyle \left(\begin{array}{cc} 1 & 2 \\ 3 & 4\\\end{array}\right) vjoin \left(\begin{array}{cc} 5 & 6 \\ 7 & 8\\\end{array}\right) = \left(\begin{array}{cc} 1 & 2 \\ 3 & 4 \\ 5 & 6\\ 7 & 8\\ \end{array}\right) $
def vJoin(m1: Matrix, m2: Matrix): Matrix = ???
5.5.4 Write a function which adds two matrices, element by element:
def matSum(m1: Matrix, m2: Matrix): Matrix = ???
5.5.5 Write a function that transposed a matrix:
$ \left(\begin{array}{ccc} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \\ \end{array}\right)^{T} = \left(\begin{array}{ccc} 1 & 4 & 7 \\ 2 & 5 & 8 \\ 3 & 6 & 9 \\ \end{array}\right)$
def transposed(m: Matrix): Matrix = ???
5.5.6 Write a function that computes the matrix product:
$ \left(\begin{array}{ccc} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \\ \end{array}\right) * \left(\begin{array}{ccc} 1 & 2 \\ 3 & 4 \\ 5 & 6 \\ \end{array}\right) = \left(\begin{array}{ccc} 22 & 28 \\ 49 & 64 \\ 76 & 100 \\ \end{array}\right)$
(For an element of the result $ a_{ij}$ the value is obtained by selecting the i-th row and the j-th column, computing the products of the elements at the same index and adding all those results up:
E. g. 22 = 1 * 1 + 2 * 3 + 3 * 5)
def matrixProd(m1: Matrix, m2: Matrix): Matrix = ???