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.

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)]