Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
pp:2025:scala:l03 [2025/03/13 13:18] cata_chiru |
pp:2025:scala:l03 [2025/03/28 12:04] (current) cata_chiru |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== Lab 3. Algebraic Datatype Definition ====== | ====== Lab 3. Algebraic Datatype Definition ====== | ||
- | Below you will find the algebraic definition of the datatype ''IList'': | + | ===== 3.1 Abstract Lists ===== |
+ | |||
+ | Below, you will find the algebraic definition of the datatype ''IList'': | ||
<code> | <code> | ||
Void : IList | Void : IList | ||
Line 7: | Line 9: | ||
</code> | </code> | ||
- | This definition has already been implemented in Scala, below. Please copy-paste this definition in your worksheet. | + | This definition has already been implemented in Scala, as shown below. Please copy-paste this definition in your worksheet. |
<code scala> | <code scala> | ||
trait IList | trait IList | ||
Line 14: | Line 16: | ||
</code> | </code> | ||
- | **4.1.** Consider the following axioms for the operator ''isEmpty''. | + | **3.1.1.** Consider the following axioms for the operator ''isEmpty''. |
<code> | <code> | ||
isEmpty : IList -> Boolean | isEmpty : IList -> Boolean | ||
Line 21: | Line 23: | ||
</code> | </code> | ||
Implement ''isEmpty'' in Scala: | Implement ''isEmpty'' in Scala: | ||
+ | |||
+ | ''! Hint:'' To pattern match the list l as a Void or a Cons use the keyword ''match'' from Scala : | ||
<code scala> | <code scala> | ||
- | def isEmpty(l: IList): Boolean = ??? | + | def isEmpty(l: IList) : Boolean = { |
+ | l match { | ||
+ | case Void => ??? | ||
+ | case Cons(x, xs) => ??? | ||
+ | } | ||
+ | } | ||
</code> | </code> | ||
- | **4.2.** Write down axioms for ''size : IList -> Int'' and implement the operator in Scala: | + | |
+ | **3.1.2.** Write down axioms for ''size : IList -> Int'' and implement the operator in Scala: | ||
<code scala> | <code scala> | ||
- | def size(l: IList): Int = ??? | + | def size(l: IList) : Int = ??? |
</code> | </code> | ||
- | **4.3.** Implement ''contains'' which checks if an element is a member of a list. | + | **3.1.3.** Implement ''contains'' to check if an element is a member of a list. |
<code scala> | <code scala> | ||
- | def contains(e: Int, l: IList): Boolean = ??? | + | def contains(e: Int, l: IList) : Boolean = ??? |
</code> | </code> | ||
- | **4.4.** Implement ''max'' which returns the largest integer from a list: | + | **3.1.4.** Implement ''max'' which returns the largest integer from a list: |
<code scala> | <code scala> | ||
- | def max(l: IList): Int = ??? | + | def max(l: IList) : Int = ??? |
</code> | </code> | ||
- | **4.5.** Implement ''take'' which returns a new list containing the first ''n'' elements of the original list: | + | **3.1.5.** Implement ''take'' which returns a new list containing the first ''n'' elements of the original list: |
<code scala> | <code scala> | ||
- | def take(n: Int)(l: IList): IList = ??? | + | def take(n: Int)(l: IList) : IList = ??? |
</code> | </code> | ||
- | **4.6.** Implement ''drop'' which returns a new list containing the original list without the first ''n'' elements: | + | **3.1.6.** Implement ''drop'' which returns a new list containing the original list without the first ''n'' elements: |
<code scala> | <code scala> | ||
- | def drop(n: Int)(l: IList): IList = ??? | + | def drop(n: Int)(l: IList) : IList = ??? |
</code> | </code> | ||
- | **4.7.** Implement ''append'' which concatenates two lists: | + | **3.1.7.** Implement ''append'' which concatenates two lists: |
<code scala> | <code scala> | ||
- | def append(l1: IList, l2: IList): IList = ??? | + | def append(l1: IList, l2: IList) : IList = ??? |
</code> | </code> | ||
- | **4.8.** (!) Implement ''last'' which returns the last element from a list: | + | **3.1.8.** (!) Implement ''last'' which returns the last element from a list: |
<code scala> | <code scala> | ||
- | def last(l: IList): Int = ??? | + | def last(l: IList) : Int = ??? |
</code> | </code> | ||
- | **4.9.** (!) Implement ''reverse''. There are two different ways to implement reverse (with direct and with tail-end recursion). Try both implementations. | + | **3.1.9.** (!) Implement ''reverse''. There are two different ways to implement reverse (with direct and with tail-end recursion). Try both implementations. |
<code scala> | <code scala> | ||
- | def reverse(l: IList): IList = ??? | + | def reverse(l: IList) : IList = ??? |
</code> | </code> | ||
- | **4.10.** Implement ''isSorted'' which checks if a list is sorted: | + | **3.1.10.** Implement ''isSorted'' which checks if a list is sorted: |
<code scala> | <code scala> | ||
- | def isSorted(l: IList): Boolean = ??? | + | def isSorted(l: IList) : Boolean = ??? |
</code> | </code> | ||
- | **4.11.** Implement ''merge'' which merges two sorted lists: | + | **3.1.11.** Implement ''merge'' which merges two sorted lists: |
<code scala> | <code scala> | ||
- | def merge(l1: IList, l2: IList): IList = ??? | + | def merge(l1: IList, l2: IList) : IList = ??? |
</code> | </code> | ||
- | **4.12.** Implement ''mergeSort'' which sorts a list: | + | **3.1.12.** Implement ''mergeSort'' which sorts a list: |
<code scala> | <code scala> | ||
- | def mergesort(l: IList) IList = ??? | + | def mergesort(l: IList) : IList = ??? |
</code> | </code> | ||
+ | |||
+ | ===== 3.2 Binary Tree ===== | ||
+ | |||
+ | The type definition for Scala is given below. Please copy-paste this definition in your worksheet. | ||
+ | <code scala> | ||
+ | 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) | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | 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: | ||
+ | <code scala> | ||
+ | def leaf_node(value : Int) : BinaryTree = ??? | ||
+ | </code> | ||
+ | |||
+ | Being given the following BinaryTree: | ||
+ | <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)))) | ||
+ | </code> | ||
+ | |||
+ | Implement the following methods for trees in Scala: | ||
+ | |||
+ | **3.2.1.** Implement ''mirror'' that mirrors the tree structure: | ||
+ | <code scala> | ||
+ | def mirror(tree: BinaryTree) : BinaryTree = ??? | ||
+ | </code> | ||
+ | |||
+ | **3.2.1.** Implement ''flatten'' that squashes the tree traversed in preorder into a list: | ||
+ | <code scala> | ||
+ | def flatten(tree: BinaryTree): List[Int] = ??? | ||
+ | </code> | ||
+ | |||
+ | **3.2.2.** Define the function ''tmap'' which is the Tree a correspondent to map::(a→b) → [a] → [b]. | ||
+ | <code scala> | ||
+ | def tmap(f: Int => Int, tree: BinaryTree) : BinaryTree = ??? | ||
+ | </code> | ||
+ | |||
+ | **3.2.3.** ! Define the function ''tfoldr'', equivalent of foldr for trees: foldr :: (a -> b -> b) -> b -> Tree a -> b | ||
+ | <code scala> | ||
+ | def tfoldr[B](f: (Int, B) => B, acc: B, tree: BinaryTree): B = ??? | ||
+ | </code> | ||
+ | |||
+ | **3.2.4.** ! Implement the ''flattening'' function using tfoldr. The order of squashing the tree does not necessarily need to match the previous exercise: | ||
+ | <code scala> | ||
+ | def flattening(tree: BinaryTree): List[Int] = ??? | ||
+ | </code> | ||
+ | |||
+ | |||
+ |