Lab 06. Polymorphism in Scala

This lab will start with the implementations discussed during lecture. Please find the polymorphic trait FList[A] below:

trait FList[A]{ // list with elements of type A
  def length: Int
  def head: A
  def tail: FList[A]
  def map[B](f: A => B): FList[B]
  // a op (b op (c op acc))
  def foldRight[B](acc: B)(op: (A,B) => B): B
  // ((acc op a) op b) op c
  def foldLeft[B](acc: B)(op: (B,A) => B): B
 
  def contains(e: A):Boolean =
    this.foldRight(false)(_ == e || _)
 
}

together with the implementations:

case class FNil[A]() extends FList[A]{
  override def length: Int = 0
  override def head: A = throw new Exception("head on empty list")
  override def tail: FList[A] = throw new Exception("head on empty list")
  override def map[B](f: A => B): FList[B] = FNil[B]
  override def foldRight[B](acc: B)(op: (A,B) => B): B = acc
  override def foldLeft[B](acc: B)(op: (B,A) => B): B = acc
}
 
case class Cons[A](x:A, xs:FList[A]) extends FList[A]{
  override def length = 1 + xs.length
  override def head:A = x
  override def tail:FList[A] = xs
  override def map[B](f: A => B): FList[B] =
    Cons(f(x),xs.map(f))
  override def foldRight[B](acc: B)(op: (A,B) => B): B =
    op(x, xs.foldRight(acc)(op))
  override def foldLeft[B](acc: B)(op: (B,A) => B): B =
    xs.foldLeft(op(acc,x))(op)
  }

Add the following methods in the trait FList and implement them. Some methods can be directly implemented in the trait, using map, foldRight, foldLeft or other functions.

  • Can you figure which ones are best implemented in the trait and which in the case classes?

6.1. indexOf determines the position of a value in the list (starting with 0)

def indexOf(e: A): Int

6.2. update creates a new list where the given position is modified with a new value:

//Cons(1,Cons(2,Cons(3,FNil()))).update(9,1) = Cons(1,Cons(9,Cons(3,FNil())))
def update(e: A, pos: Int): FList[A]

6.3. append concatenates this list to another:

def append(l: FList[A]): FList[A]

6.4. reverse returns the reversed list:

def reverse: FList[A]

6.5. last returns the last element of the list:

def last: A

6.6. filter filters the elements of the list:

def filter(p: A => Boolean): FList[A]

6.7. zip combines two lists into a list of pairs. If either list is larger, the remaining elements are discarded.

// Cons(1,(Cons(2,Cons(3,FNil()))).zip(Cons(true,Cons(false,Cons(true,FNil())))) =
// Cons((1,true),Cons((2,false),Cons((3,true),FNil())))
def zip[B](l: FList[B]): FList[(A,B)]

6.8. insSorted inserts an element into a sorted list so that the result is a sorted list.

def insSorted(f: A => Int)(e: A): FList[A]

6.9. sortBy sorts a list using insertion sort.

def sortBy(f: A => Int): FList[A]

6.10 (!) Implement a method pack which encodes a sorted list as follows:

[1,1,1,2,3,4,4,5,6].pack = [(1,3),(2,1),(3,1),(4,2),(5,1),(6,1)]
def pack: FList[(A,Int)]