Last updated on

Exercise Set

Points
100 points
Files to submit
src/main/scala/exercises/CSVParser.scala
src/main/scala/exercises/CounterStyle.scala

Welcome to the CS-214 final exam! This first problem is a set of small exercises.

Make sure that all exercises compile successfully when you submit (try sbt compile), otherwise you will get 0 points for all exercises!

CSV Parser (20 points)

Topics
Safe Effects, Exception Handling

CSV is a line-oriented data format: a CSV file stores multiple records, one on each line. Each record consists of multiple fields, separated by the comma character (,).

Example The following CSV file contains three rain measurements:
17,2023-11-24T15:13:04.725Z,0.25
68,2023-11-25T15:00:11.948Z,3.64
77,2023-11-25T18:59:23.176Z,2.83

These records can be parsed into the following Scala values:

val expected = List(
  Measurement(17, dateFormat.parse("2023-11-24T15:13:04.725Z"), 0.25),
  Measurement(68, dateFormat.parse("2023-11-25T15:00:11.948Z"), 3.64),
  Measurement(77, dateFormat.parse("2023-11-25T18:59:23.176Z"), 2.83)
)

exercises/./src/main/scala/csvparser.worksheet.sc

Where each Measurement consists of an ID of the originating weather station, the measurement time and the rain amount.

/** The data stored in each row of the CSV file. */
case class Measurement(stationId: Int, when: Date, rainAmount: Double)

exercises/./src/main/scala/exercises/CSVParser.scala

This example can be found in the worksheet csvparser.worksheet.sc.

You will complete the implementation of CSVParser, a parser class for CSV files containing rain measurement history. Within the class, you are provided the parseLine method, which parses a single line of CSV into a single Measurement.

Parsing can fail, and parseLine will throw an exception of type ParseLineError in such case.

💪 Task: Implement parseFile for CSVParser

Your task is to implement parseFile, which takes a filepath and attempts to parse the given file as a CSV file. It returns a list of Measurementss.

class CSVParser(using fs: FileSystem):
  /** Parses the file given by `filepath`.
    * @return
    *   a list of parsed [[Measurement]], one for each line.
    * @throws ParsingError
    *   if there is a parsing error on a certain line. If multiple lines are
    *   failing, include the _first_ line with the error.
    * @throws java.io.FileNotFoundException
    *   if filepath cannot be opened by the filesystem.
    * @note
    *   Do not forget to close any opened [[File]] **exactly** once!
    * @note
    *   Do _not_ throw any [[ParseLineError]].
    */
  def parseFile(filepath: String): List[Measurement] =
    ???

exercises/./src/main/scala/exercises/CSVParser.scala

If a parsing error occurs, we need to report on where it does to the caller. We do that by throwing a ParseError:

/** A parsing error that occurs on the specified line (starting from 1). */
case class ParseError(error: ParseLineError, line: Int)
    extends Exception(s"Error while parsing on line $line: $error", error)

exercises/./src/main/scala/exercises/CSVParser.scala

To open a file, you can use the open method of the given FileSystem. Once a File is opened, the getLines() method returns a LazyList[String] over the lines of the file. After reading a File, it must be closed with the close() method.

Any opened File must be closed exactly once.

You may find the documentation of FileSystem and File (also in CSVParser.scala) helpful.

You can run tests for this exercise using the testOnly command.

sbt:exercises> testOnly -- *csvparser*

Counter styles (40 points)

Topics
Recursion, Laziness, Specifications

Many applications have support for “ordered lists”. For example, a grocery list app might display the ordered list Bread, Potatoes, Cabbage thus:

1. Bread
2. Potatoes
3. Cabbage

Alternatively, the same list could be displayed using letters as list markers instead:

a. Bread
b. Potatoes
c. Cabbage

On the web, the choice of marker before elements of an ordered list is called a counter style. In this exercise, we will represent a counter style as a lazy list of all values that the counter can take:

type CounterStyle[A] = LazyList[A]

exercises/src/main/scala/exercises/CounterStyle.scala

Counter specifications

We are interested in the following counter styles, whose specifications are directly quoted from the official W3C specification of CSS.

cyclic counters
The cyclic counter system cycles repeatedly through its provided symbols, looping back to the beginning when it reaches the end of the list.

For example, given the symbols -, +, *, the cyclic counter system produces -, +, *, -, +, *, -, +, *, - etc.

symbolic counters (repeating)
The symbolic counter system cycles repeatedly through its provided symbols, doubling, tripling, etc. the symbols on each successive pass through the list. […] It can be used for footnote-style markers.

For example, given the symbols * and †, the cyclic counter system produces *, †, **, ††, ***, †††, ****, etc.

fixed counters (finite)
The fixed counter system runs through its list of counter symbols once, then falls back. It is useful for representing counter styles that only have a finite number of representations. For example, Unicode defines several limited-length runs of special characters meant for lists, such as circled digits.

For example, given the symbols 1, a, *, and the fallback cyclic(x, y), the fixed counter system produces 1, a, *, x, y, x, y, x, etc.

numeric counters
The numeric counter system interprets the list of counter symbols as digits to a “place-value” numbering system.

For example, given the digits 0, 1, 2, the numeric counter system produces 1, 2, 10, 11, 12, 20, 21, etc.

alphabetic counters
The alphabetic counter system interprets the list of counter symbols as digits to an alphabetic numbering system. […] Alphabetic numbering systems […] appear in many spreadsheet programs to number columns.

For example, given the letters x, y, z, the alphabetic counter system produces x, y, z, xx, xy, xz, yx, yy, yz, etc.

💪 Task: Counter styles (40 pts)

Implement each of the counter styles listed above (i.e., lazy lists of counter values, starting from the first one).

You can run tests for this exercise using the testOnly command.

sbt:exercises> testOnly -- "*counterstyle*"

A few tests, marked difficult, may take a long time to complete if your solution is inefficient. They are disabled by default and will not run nor give you points until you enable them. Once you’re ready, you can enable them by changing enableDifficultTests to true.

object CounterStyle:
  /** Set this value to true to enable difficult tests. */
  val enableDifficultTests: Boolean =
    false

exercises/./src/main/scala/exercises/CounterStyle.scala

Cyclic

def cyclic[T](symbols: Seq[T]): CounterStyle[T] =
  require(symbols.nonEmpty)
  ???

exercises/src/main/scala/exercises/CounterStyle.scala

Symbolic

def symbolic[T](symbols: Seq[T]): CounterStyle[List[T]] =
  require(symbols.nonEmpty)
  ???

exercises/src/main/scala/exercises/CounterStyle.scala

Fixed

def fixed[T](symbols: Seq[T], fallback: CounterStyle[T]): CounterStyle[T] =
  ???

exercises/src/main/scala/exercises/CounterStyle.scala

Numeric

def numeric[T](digits: Seq[T]): CounterStyle[List[T]] =
  require(digits.nonEmpty && digits.tail.nonEmpty)
  ???

exercises/src/main/scala/exercises/CounterStyle.scala

Alphabetic

def alphabetic[T](letters: Seq[T]): CounterStyle[List[T]] =
  require(letters.nonEmpty)
  ???

exercises/src/main/scala/exercises/CounterStyle.scala