Lab 3. Algebraic Datatype Definition

Below, you will find the algebraic definition of the datatype IList:

Void : IList
Cons : Int x IList -> IList

This definition has already been implemented in Scala, as shown below. Please copy-paste this definition in your worksheet.

trait IList 
case object Void extends IList
case class Cons(x: Int, xs: IList) extends IList

3.1.1. Consider the following axioms for the operator isEmpty.

isEmpty : IList -> Boolean
isEmpty(Void) = true
isEmpty(Cons(h,t)) = false.

Implement isEmpty in Scala:

! Hint: To pattern match the list l as a Void or a Cons use the keyword match from Scala :

def isEmpty(l: IList) : Boolean = {
    l match {
        case Void => ???
        case Cons(x, xs) => ???
    }
}

3.1.2. Write down axioms for size : IList → Int and implement the operator in Scala:

def size(l: IList) : Int = ???

3.1.3. Implement contains to check if an element is a member of a list.

def contains(e: Int, l: IList) : Boolean = ???

3.1.4. Implement max which returns the largest integer from a list:

def max(l: IList) : Int = ???

3.1.5. Implement take which returns a new list containing the first n elements of the original list:

def take(n: Int)(l: IList) : IList = ???

3.1.6. Implement drop which returns a new list containing the original list without the first n elements:

def drop(n: Int)(l: IList) : IList = ???

3.1.7. Implement append which concatenates two lists:

def append(l1: IList, l2: IList) : IList = ???

3.1.8. (!) Implement last which returns the last element from a list:

def last(l: IList) : Int = ???

3.1.9. (!) Implement reverse. There are two different ways to implement reverse (with direct and with tail-end recursion). Try both implementations.

def reverse(l: IList) : IList = ???

3.1.10. Implement isSorted which checks if a list is sorted:

def isSorted(l: IList) : Boolean = ???

3.1.11. Implement merge which merges two sorted lists:

def merge(l1: IList, l2: IList) : IList = ???

3.1.12. Implement mergeSort which sorts a list:

def mergesort(l: IList) : IList = ???

The binary tree type definition for Scala is given below. Please copy-paste this definition in your Main.scala file from your laboratory project, in order for printTree to work properly. Call print from the function main and click the “run” text above the definition of main to see the result in terminal.

trait BinaryTree {
    override def toString: String = super.toString: String
}
 
case object TVoid extends BinaryTree {
    override def toString: String = "-"
}
 
case class Node(left: BinaryTree, info: Int, right: BinaryTree) extends BinaryTree {
  override def toString: String = {
    def printTree(
                   tree: BinaryTree,
                   prefix: String = "",
                   isLeft: Boolean = true
                 ): String =
      tree match {
        case TVoid =>
          ""
        case Node(l, value, r) =>
          val rightStr =
            printTree(r, prefix + (if (isLeft && prefix.nonEmpty) "│   " else "    "), isLeft = false)
          val nodeStr =
            prefix + (if (tree != this) if (isLeft) "└── " else "┌── " else "    ") + value.toString + "\n"
          val leftStr =
            printTree(l, prefix + (if (isLeft) "    " else "│   "))
          rightStr + nodeStr + leftStr
      }
 
    '\n' + printTree(this)
  }
}

A Binary Tree can either be a TVoid object (equivalent to NULL in C), or a Node with its information as an Int and two other trees as children (left and right).

3.2.0. Implement leaf_node that receives an Int and returns a leaf with that integer as the node's value:

def leaf_node(value : Int) : BinaryTree = ???

Being given the following BinaryTree:

val arborica = Node(Node(leaf_node(-1), 5, TVoid), 1, Node(leaf_node(4), 2, Node(leaf_node(3), 6, leaf_node(7))))

Implement the following methods for trees in Scala:

3.2.1. Implement mirror that mirrors the tree structure:

def mirror(tree: BinaryTree) : BinaryTree = ???

3.2.1. Implement flatten that squashes the tree traversed in preorder into a list:

def flatten(tree: BinaryTree): List[Int] = ???

3.2.2. Define the function tmap which is the Tree a correspondent to map::(a→b) → [a] → [b].

def tmap(f: Int => Int, tree: BinaryTree) : BinaryTree = ???

3.2.3. ! Define the function tfoldr, equivalent of foldr for trees: foldr :: (a → b → b) → b → Tree a → b

def tfoldr[B](f: (Int, B) => B, acc: B, tree: BinaryTree): B = ???

3.2.4. ! Implement the flattening function using tfoldr. The order of squashing the tree does not necessarily need to match the previous exercise:

def flattening(tree: BinaryTree): List[Int] = ???