Edit this page Backlinks This page is read only. You can view the source, but not change it. Ask your administrator if you think this is wrong. ====== 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. <code scala> trait Nat case object Zero extends Nat case class Succ(x: Nat) extends Nat </code> **5.1.1** Write a function which takes two natural numbers, and returns their sum. <code scala> def add(x: Nat, y: Nat): Nat = ??? </code> **5.1.2** Write a function which takes two natural numbers, and returns their product. <code scala> def multiply(x: Nat, y: Nat): Nat = ??? </code> **5.1.3** Write a function which takes an int and converts it to a Nat. <code scala> def toNat(x: Int): Nat = ??? </code> ==== 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. <code scala> def realrealtrycatch(t: => Option[Int], c: => Int): Int = { ??? } </code> **(!) 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. <code scala> def toNatOpt(x: Int): Option[Nat] = ??? </code> **(!) 5.2.3** Refactor the function add(), so that it takes two "containers" of Nats and returns a "container" of a Nat. <code scala> def addOpt(x: Option[Nat], y: Option[Nat]): Option[Nat] = ??? </code> ==== 5.3 Binary Trees ==== Given the implementation of binary trees from the previous lab: <code scala> 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) } </code> And arborică: <code scala> 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 </code> Solve the next few exercises: **5.3.1** Write a function which takes a BinaryTree and returns its depth. <code scala> def depth(tree: BTree): Int = ??? </code> **5.3.2** Write a function which takes a BinaryTree and returns the number of nodes in its subtree. <code scala> def subtree(tree: BTree): Int = ??? </code> **5.3.3** Write a function which takes a BinaryTree and returns the number of nodes with an even number of children. <code scala> def evenChildCount(tree: BTree): Int = ??? </code> **5.3.4** Write a function which takes a BinaryTree and returns the number of nodes whose values follow a certain rule. <code scala> def countNodes(tree: BTree, cond: Int => Boolean): Int = ??? </code> **(!) 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 . <code scala> def append(tree1: BTree, tree2: BTree): Option[BTree] = ??? </code> ==== 5.4 Expression evaluation ==== Given the following implementation of expressions, solve the next few exercises. <code scala> 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 </code> **5.4.1** Write a function which takes an Expression and evaluates it. <code scala> def evaluate(e: Expr): Int = ??? </code> **5.4.2** Write a function which takes an Expression and simplifies it. (Ex. a * (b + c) -> remove parentheses -> ab + ac) <code scala> def simplify(e: Expr): Expr = ??? </code> **5.4.3** Write a function which takes an Expression and removes 'useless' operations. (Ex. a * 1 -> a, a + 0 -> a) <code scala> def optimize(e: Expr): Expr = ??? </code> <hidden> If you work outside of worksheets, you can define the trait as: <code scala> trait Expr { def + (that: Expr): Expr = Add(this, that) def * (that: Expr): Expr = Mul(this, that) } 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 </code> With the operators defined, you can create expressions writting: <code scala> (Atom(1) + Atom(2) * Atom(3)) + Atom(4) </code> instead of: <code scala> Add(Add(Atom(1), Mult(Atom(2), Atom(3))), Atom(4)) </code> </hidden> ==== 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 $math[ \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: <code scala> type Matrix = List[List[Int]] </code> which makes the type-name ''Matrix'' stand for ''[ [Integer] ]''. **5.5.1** Write a function that computes the scalar product with an integer: $math[ \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)] <code scala> def scalarProd(m: Matrix)(v: Int): Matrix = ??? </code> **5.5.2** Write a function which adjoins two matrices by extending rows (horizontally): $math[ \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) ] <code scala> def hJoin(m1: Matrix, m2: Matrix): Matrix = ??? </code> **5.5.3** Write a function which adjoins two matrices by adding new rows (vertically): $math[ \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) ] <code scala> def vJoin(m1: Matrix, m2: Matrix): Matrix = ??? </code> **5.5.4** Write a function which adds two matrices, element by element: <code scala> def matSum(m1: Matrix, m2: Matrix): Matrix = ??? </code> **5.5.5** Write a function that transposed a matrix: $math[ \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)] <code scala> def transposed(m: Matrix): Matrix = ??? </code> **5.5.6** Write a function that computes the matrix product: $math[ \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 $math[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) <code scala> def matrixProd(m1: Matrix, m2: Matrix): Matrix = ??? </code>