Worksheet Algebraic Data Types and Pattern Matching

Table of Contents

1. Classes in Scala

1.1. Java Translation

Translate the following Java class definition into a corresponding Scala class definition. You will need a companion object definition to model the static components.

class Counter {
  private static int numCounters = 0;

  final int id;
  int count;

  Counter (int initial) {
    this.id = numCounters;
    this.count = initial;
    numCounters++;
  }

  static int getNumCounters () {
    return numCounters;
  }

  int getId () {
    return this.id;
  }

  int getNextCount () {
    return this.count++;
  }
}

Solution: Classes - Java Translation

2. Algebraic Data Types via Case classes in Scala

2.1. MyList Case Class

Define your own MyList class in Scala (as in the slides).

Now implement the following functions:

enum MyList[+X] {
  ...
}
import MyList._

  def length [X] (xs:MyList[X]) : Int = {
    ...
  }

  def toList [X] (xs:MyList[X]) : List[X] = {
    ...
  }

  def fromList [X] (xs:List[X]) : MyList[X] = {
    ...
  }

  def append [X] (xs:MyList[X], ys:MyList[X]) : MyList[X] = {
    ...
  }

  def map [X,Y] (xs:MyList[X], f:X=>Y) : MyList[Y] = {
    ...
  }

Solution: MyList Case Class

2.2. Binary Tree Case Class

Define your own Tree class for binary trees in Scala (as in the slides).

Now implement the following functions:

enum Tree[+X] {
  case Leaf
  case Node(l:Tree[X], c:X, r:Tree[X])
}
import Tree.{Leaf,Node}

val tree1:Tree[Int] = Leaf

val tree2:Tree[Int] = Node (Leaf, 5, Leaf)

val tree3:Tree[Int] = Node (Node (Leaf, 2, Leaf), 3, Node (Leaf, 4, Leaf))

// Find the size of a binary tree.  Leaves have size zero here.
def size [X] (t:Tree[X]) : Int = {
  ...
}

// Insert a number into a sorted binary tree.
def insert [X] (x:X, t:Tree[X], lt:(X,X)=>Boolean) : Tree[X] = {
  ...
}

// Put the elements of the tree into a list using an in-order traversal.
def inorder [X] (t:Tree[X]) : List[X] = {
  ...
}

def map [X,Y] (t:Tree[X], f:X=>Y) : Tree[Y] = {
  ...
}

Solution: Binary Tree Case Class

3. Solutions

3.1. Solution: Classes - Java Translation

object Counter:
  private var numCounters = 0

  def getNumCounters : Int = numCounters
  def incNumCounters : Unit = numCounters = numCounters + 1

class Counter (initial:Int):
  private val id:Int = Counter.getNumCounters
  private var count:Int = initial
  Counter.incNumCounters

  def getId () : Int = id
  def getNextCount () : Int = { val tmp = count; count = count + 1; tmp }

3.2. Solution: MyList Case Class

enum MyList[+X]:
  case MyNil
  case MyCons(head:X, tail:MyList[X])
import MyList.*

def length [X] (xs:MyList[X]) : Int =
  xs match
    case MyNil => 0
    case MyCons (_, ys) => 1 + length (ys)

def toList [X] (xs:MyList[X]) : List[X] =
  xs match
    case MyNil => Nil
    case MyCons (y, ys) => y::toList(ys)

def fromList [X] (xs:List[X]) : MyList[X] =
  xs match
    case Nil => MyNil
    case y::ys => MyCons (y, fromList (ys)) 

def append [X] (xs:MyList[X], ys:MyList[X]) : MyList[X] =
  xs match
    case MyNil => ys
    case MyCons (z, zs) => MyCons (z, append (zs, ys)) 

def map [X,Y] (xs:MyList[X], f:X=>Y) : MyList[Y] =
  xs match
    case MyNil => MyNil
    case MyCons (y, ys) => MyCons (f (y), map (ys, f)) 

val xs = MyCons(11, MyCons(21, MyCons(31, MyNil)))
val len = length (xs)

3.3. Solution: Binary Tree Case Class

import javax.swing.text.AbstractDocument.LeafElement
enum Tree[+X]:
  case Leaf
  case Node(l:Tree[X], c:X, r:Tree[X])
import Tree.*

val tree1:Tree[Int] = Leaf

val tree2:Tree[Int] = Node (Leaf, 5, Leaf)

val tree3:Tree[Int] = Node (Node (Leaf, 2, Leaf), 3, Node (Leaf, 4, Leaf))

// Find the size of a binary tree.  Leaves have size zero here.
def size [X] (t:Tree[X]) : Int =
  t match
    case Leaf => 0
    case Node (t1, _, t2) => size (t1) + 1 + size (t2)

// Insert a number into a sorted binary tree.
def insert [X] (x:X, t:Tree[X], lt:(X,X)=>Boolean) : Tree[X] =
  t match
    case Leaf => Node (Leaf, x, Leaf)
    case Node (t1, c, t2) if (lt (x, c)) => Node (insert (x, t1, lt), c, t2)
    case Node (t1, c, t2) if (lt (c, x)) => Node (t1, c, insert (x, t2, lt))
    case Node (t1, c, t2)                => Node (t1, c, t2)

// Put the elements of the tree into a list using an in-order traversal.
def inorder [X] (t:Tree[X]) : List[X] =
  t match
    case Leaf => Nil
    case Node (t1, c, t2) => inorder (t1) ::: List (c) ::: inorder (t2)

def map [X,Y] (t:Tree[X], f:X=>Y) : Tree[Y] =
  t match
    case Leaf => Leaf
    case Node (t1, c, t2) => Node (inorder (t1), f (c), inorder (t2))

Author: James Riely

Created: 2025-04-09 Wed 15:33

Validate