Last updated on
Exercise Set
- Points
- 100 points
- Files to submit
src/main/scala/exercises/CSVParser.scalasrc/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 fallbackcyclic(x, y), the fixed counter system produces1,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 produces1,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 producesx,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