The type List[A] only allows sequential access to elements. For example, you
can’t get to the third element until you’ve been through the first two.
Instead, if you could recur on sub-lists that were significantly smaller
and run each recursive call on on a different processor core, you could
obtain significant speedup. This requires a different data structure,
which is what we’ll explore in this assignment. Remarkably, this
new data structure, will support the same operations as a list.
You should create a series of directories that look like this:
./joinlists
|-- build.sbt
`-- project
`-- plugins.sbt
`-- src
|-- main
| `-- scala
| `-- your solution goes here
`-- test
`|-- scala
`-- your tests goes here
Your build.sbt file must have exactly these lines:
name := "joinlists"
scalaVersion := "2.11.2"
libraryDependencies += "edu.umass.cs" %% "cmpsci220" % "1.1"
libraryDependencies += "org.scalatest" %% "scalatest" % "2.2.1" % "test"The plugins.sbt file must have exactly this line:
addSbtPlugin("edu.umass.cs" % "cmpsci220" % "2.2")When we grade your work, we will delete your build.sbt file and use the
one above. So, if you change it, you risk getting a zero.
You’ll be working with the JoinList[A] type, which is part
of the cmpsci220.hw.joinlists package. It has three constructors:
Empty() represents an empty listSingleton(x) represents a list with one element xJoin(lst1, lst2, length) represents lst1 appended to lst2. The
length field is the total number of elements in the list.It should be clear that it is very cheap to append two join lists: you simply
use the Join constructor. It is also cheap to calculate the length of a
join list, since it is stored at each non-trivial node of the tree.
The package includes two simple, helper functions:
length[A](lst: JoinList[A]): Int produces the length of a list. It assumes
that the length field is accurate.
join[A](lst1: JoinList[A], lst2: JoinList[A]): JoinList[A] is what’s
known as a smart constructor. If either lst1 or lst2 is Empty(),
it just produces the other list. More significantly, instead of naively
joining the two lists, it produces a size-balanced join list. If you’re
interested, go read the code. It is quite cool.
Finally, since join lists representat for lists, the package has two functions to convert between join lists and lists:
toList[A](lst: JoinList[A]): List[A] converts a join list into a Scala
list. i.e., not the cmpsci220.List type. It is a simple function that
turns Join(lst1, lst2)s into list-appends (the ++ operator). This
operation can be very expensive, but is useful for testing.
fromList[A](lst: List[A]): JoinList[A] converts a Scala list into a join
list by repeatedly applying the join smart constructor.
Both toList and joinList are provided for testing only. You must
not use them in your solution. If you do, you risk getting a zero.
In the file src/main/scala/Solution.scala, you must define an
object called Solution that implements the JoinListFunctions trait.
You can use the this template:
// src/main/scala/Solution.scala
import cmpsci220.hw.joinlists._
object Solution extends JoinListFunctions {
def max[A](lst: JoinList[A], compare: (A, A) => Boolean): Option[A] = {
throw new UnsupportedOperationException("not implemented")
}
def map[A,B](f: A => B, lst: JoinList[A]): JoinList[B] = {
throw new UnsupportedOperationException("not implemented")
}
def filter[A](pred: A => Boolean, lst: JoinList[A]): JoinList[A] = {
throw new UnsupportedOperationException("not implemented")
}
def first[A](lst: JoinList[A]): Option[A] = {
throw new UnsupportedOperationException("not implemented")
}
def rest[A](lst: JoinList[A]): Option[JoinList[A]] = {
throw new UnsupportedOperationException("not implemented")
}
def nth[A](lst: JoinList[A], n: Int): Option[A] = {
throw new UnsupportedOperationException("not implemented")
}
}Here are the specifications of these functions:
max(lst, compare) produces the maximum value in lst. The second argument
is a comparator. If compare(x, y) == true, then x is greater than y. If
lst is empty, max produces None.
map(f, lst) produces a new lst which has the same structure as lst,
but with f applied to every element.
filter(pred, lst) removes all the elements of lst that do not satisfy
the given predicate.
first(lst) and rest(lst) produce the head and tail, respectively,
of lst if `lst is non-empty.
nth(lst, i) produces the nth element of the list.
Here is a trivial test suite that simply checks to see if you’ve defined
the Solution object with the right type:
class TrivialTestSuite extends org.scalatest.FunSuite {
test("The solution object must be defined") {
val obj : cmpsci220.hw.joinlists.JoinListFunctions = Solution
}
}You should place this test suite in src/test/scala/TrivialTestSuite.scala.
If this test suite does not run as-is, you risk getting a zero.
Use the submit command within sbt to create submission.tar.gz. Upload
this file to Moodle.