diff --git a/dryrun/README.md b/dryrun/README.md deleted file mode 100644 index 4402ade9db0d1bb37945daf67171c36cb854a4e8..0000000000000000000000000000000000000000 --- a/dryrun/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# Exam dry run - -Wednesday, 16 December 2020, 13:15 - 15:00 - -This exam is open book in the sense that you are allowed to consult the PDF slides for all lectures in this course. - -The exam consists of a list of programming assignments that you must solve in order, using the usual tools from the class (git, sbt, and your favorite text editor). Each student will be given a unique question order. Your personal exam is dynamically created, so that every time you solve a question, you are given the next one. You are not allowed to go back to your previous questions after you start working on the next one. We do not expect that you can necessarily solve all questions in this limited time, but you should do your best. - -In addition to problem statements, each problem comes with a set of automated tests, just like for the labs. If your solution passes all the given tests, it is most likely correct and you can expect to obtain a full grade for that question. Also make sure to follow any additional requirements listed in the problem statement. Some of those requirements, such as avoiding the use of vars, are not covered by our automated tests and will be graded manually. - -The TAs will be available on Discord during the exam in case you have questions or need help. You are only allowed to use our public discord channel for class and no other communication. It is your responsibility to ensure that you have access to reliable internet connection, hardware, and software for the duration of the exam. Also make sure that you are comfortable using the usual tools from the class, so that you can focus on the exam. - -*No cheating*: Each student must solve the exam individually. Consulting with anyone except the teaching staff of this course during the exam or making your solution available to anyone is considered cheating and a reason for disciplinary action. We will use plagiarism detection tools on your solution. We reserve the right to follow up with some of you on zoom after the scheduled slot and ask you about solutions to your or similar questions, whether or not we suspect you cheated; this possibility is a normal part of the examination this year. - -## How to obtain questions - -Assignments are available on gitlab. Please clone the course repository at the start of the exam to have access to problem statements in case you lose your internet connection: - -``` -git clone https://gitlab.epfl.ch/lamp/cs210.git -``` - -Each assignment is published similarly to labs: the problem statements are given as markdown files in the following directory: https://gitlab.epfl.ch/lamp/cs210/-/tree/master/dryrun; the code skeleton for each problem is available in a separate branch of your private gitlab repository. Each student can find a link to their first exam question in the following .pdf file: https://gitlab.epfl.ch/lamp/cs210/-/blob/master/dryrun/first-questions.pdf. Links to subsequent questions are distributed after solving each question. - -## How to submit questions - -Your answers are submitted by pushing your code on gitlab, just like the labs. For detailed information, refer to labs submission instructions: https://gitlab.epfl.ch/lamp/cs210/-/blob/master/labs/grading-and-submission.md#committing-and-pushing-your-code. After you submit your solution, you will receive a preliminary grade for that question, as well as the link to your next question. This information will be available in the GitLab CI, the following screenshot show how to obtain the link to your next question: - -Starting from your GitLab project page, click on the branch corresponding to your current question: - -![](images/1.png) - -Then click on the circular icon next to your commit hash: - -![](images/2.png) - -The CI pipeline has three stages; the first one gives you the link to your next question; the second one compiles your code; the third one gives you a grade using the provided automated tests: - -![](images/3.png) - -Your next question is provided as follows: - -![](images/4.png) - -You are allowed to push multiple times for the same question (only the last commit will be considered). However, you are not allowed to go back to your previous questions after you start working on the next one. More precisely, we will ignore commits that do not respect your question order. - -## How to run tests - -Refer to labs submission instructions for detailed information on how to run the tests: https://gitlab.epfl.ch/lamp/cs210/-/blob/master/labs/grading-and-submission.md#local-tests-and-grading - -## Recommended workflow summary - -After you have obtained a question, do the following: - -1. read the requirements carefully -2. write the solution taking the requirements into account -3. make sure your solution compiles -4. make sure your solution passes the local tests on your machine; if it does not, repeat previous steps -5. submit your solution using git; if it does not pass the tests, repeat the previous steps -6. if your solution is accepted as correct by CI, request the next question - -If you feel you are spending more time on the question and will likely not solve it, then go to step 6 even if your solution is not accepted to get a chance at solving subsequent question. Note that you will be losing 2 points when you do not solve a question, so take this into account. - -## How do we grade the exam - -Each question you solve correctly gives you 10 points. A question is solved correctly if it passes all the tests including those we made available to you (a maximum grade of 10/10), and the solution fulfills all the requirements listed in the problem statement (such as not using vars). If you do not manage to solve a particular question, you can move on to the next one by submitting your partial solution or even an empty commit. Those incorrect solutions are penalized with -2 points. Given the exam format, those points will then be converted to traditional grades depending on the overall exam performance. Our aim is that a student who mastered the material can solve approximately 5 questions in this time period and obtain a maximum grade, taking into account small variations in question difficulty. This estimate is provided only to help your time planning. diff --git a/dryrun/e1.md b/dryrun/e1.md deleted file mode 100644 index 2b8e948d84f4f5ebecfcd981d1c33d28d58b8707..0000000000000000000000000000000000000000 --- a/dryrun/e1.md +++ /dev/null @@ -1,25 +0,0 @@ -# Setup - -You can use the following commands to make a fresh clone of your repository: - -``` -git clone -b e1 git@gitlab.epfl.ch:lamp/students-repositories-fall-2020/cs210-GASPAR.git cs210-e1 -cd cs210-e1 -``` - -# Be functional! - -This course is about **functional** programming, therefore you're not allowed to use the following -constructs in this assignment: -- `var` -- `while` -- `return` -- Any class in the `scala.collection.mutable` package - -# Exercise 1: List functions - -Implement a function that takes a list `ls` as argument, and returns a list of all the suffixes of `ls`.That is, given a list `List(a,b,c,...)` it returns `List(List(a,b,c,...), List(b,c,...), List(c,...),List(...), ..., List())`. Implement the function recursively using only `Nil`(empty), `::`(cons) and pattern matching. - -```scala -def tails(ls: List[Int]): List[List[Int]] -``` diff --git a/dryrun/e2.md b/dryrun/e2.md deleted file mode 100644 index 988eb8c23c26bafa30fda83f0cb13e0b9742f307..0000000000000000000000000000000000000000 --- a/dryrun/e2.md +++ /dev/null @@ -1,33 +0,0 @@ -# Setup - -You can use the following commands to make a fresh clone of your repository: - -``` -git clone -b e2 git@gitlab.epfl.ch:lamp/students-repositories-fall-2020/cs210-GASPAR.git cs210-e2 -cd cs210-e2 -``` -# Be functional! - -This course is about **functional** programming, therefore you're not allowed to use the following -constructs in this assignment: -- `var` -- `while` -- `return` -- Any class in the `scala.collection.mutable` package - -# Exercise 2: Graph Reachability - -Consider the following case class definitions: - -```scala -case class Node(id: Int) -case class Edge(from: Node, to: Node) -``` - -Let us represent a directed graph `G` as the list of all its edges (of type `List[Edge]`). We are interested in computing the set of all nodes reachable in **exactly** `n` steps from a set of initial nodes. Write a `reachable` function with the following signature to provide this functionality: - -```scala -def reachable(n: Int, init: Set[Node], edges: List[Edge]): Set[Node] -``` - -You can assume that `n` >= 0. diff --git a/dryrun/e3.md b/dryrun/e3.md deleted file mode 100644 index fd2d4135f16f16375a71b6aadd5acc2c6e3551f6..0000000000000000000000000000000000000000 --- a/dryrun/e3.md +++ /dev/null @@ -1,38 +0,0 @@ -# Setup - -You can use the following commands to make a fresh clone of your repository: - -``` -git clone -b e3 git@gitlab.epfl.ch:lamp/students-repositories-fall-2020/cs210-GASPAR.git cs210-e3 -cd cs210-e3 -``` -# Be functional! - -This course is about **functional** programming, therefore you're not allowed to use the following -constructs in this assignment: -- `var` -- `while` -- `return` -- Any class in the `scala.collection.mutable` package - -# Exercise 3: For-comprehensions - -You are given three classes (`Student`, `Exam` and `Course` which are defined below) and the method `generatePassedExams`, which from a given list of students and a list of courses, generates a list of students and all their successfully passed courses together with the corresponding grade. A course is considered assuccessfully passed if the grade for that course is greater than 2. - -```scala -case class Student(name: String, exams: List[Exam]) -case class Exam(courseId: String, grade: Double) -case class Course(id: String, name: String) - -def generatePassedExams(students: List[Student], courses: List[Course]): List[(String, String, Double)] = { - for{ - s <- students - e <- s.exams - if e.grade > 2 - c <- courses - if e.courseId == c.id - } yield(s.name, c.name, e.grade) -} -``` - -Your task is to rewrite the method `generatePassedExams` to use `map`, `flatMap` and `filter` instead of the for-comprehension. The resulting method should of course have the same result as the for-comprehension above. diff --git a/dryrun/e4.md b/dryrun/e4.md deleted file mode 100644 index c3f54e1d3540ffa273f27c74d9c46a34bded7d97..0000000000000000000000000000000000000000 --- a/dryrun/e4.md +++ /dev/null @@ -1,48 +0,0 @@ -# Setup - -You can use the following commands to make a fresh clone of your repository: - -``` -git clone -b e4 git@gitlab.epfl.ch:lamp/students-repositories-fall-2020/cs210-GASPAR.git cs210-e4 -cd cs210-e4 -``` -# Be functional! - -This course is about **functional** programming, therefore you're not allowed to use the following -constructs in this assignment: -- `var` -- `while` -- `return` -- Any class in the `scala.collection.mutable` package - -# Exercise 4: Pattern matching and recursion - -In this exercise you will be working with perfectly balanced trees, defined as follows: - -```scala -trait Perfect[A] -case class Empty[A]() extends Perfect[A] -case class Layer[A](elem: A, next: Perfect[(A, A)]) extends Perfect[A] -``` - -Perfect trees are always perfectly balanced by construction. Unlike the traditional tree data-structure, which consists of nodes and leaves, perfect trees are made of several layers of increasing size and a single empty treeat the bottom. For instance, the perfect tree containing the numbers 1 to 7 is defined as follows: -```scala - val exampleTree: Perfect[Int] = - Layer(1, // 1st layer, with 1 element - Layer((2, 3), // 2nd layer, with 2 elements - Layer(((4, 5), (6, 7)), // 3rd layer, with 4 elements - Empty() // empty tree at the bottom - ) - ) - ) -``` - -Note that each layer holds twice as many elements as its parent layer, and that elements are packed into nested pairs. This structure emerges from the fact that the next layer of `Perfect[A]` is defined to be a `Perfect[(A, A)]`, that is, a tree with pairs of `A`-s as its elements. We call this data-structure a perfectly balanced tree because it always contains exactly $2^n−1$ elements, where n is the number of layers. - -Implement the `toList` method that transforms a perfectly balanced tree into a list. Your implementation must be written directly in the body of trait `Perfect`, as opposed to being in the body of the case classes `Empty` and `Layer`. The order of the elements in the returned list should correspond to a layer by layer traversal of the tree. In other words, calling `.toList` on the example tree defined in the introduction should return `List(1, 2, 3, 4, 5, 6, 7)`. - -**Hint 1**: When creating or concatenating lists, make sure that all elements are of the same type. -**Hint 2**: Don’t worry about performance, aim for the simplest solution possible. A correct but slow solution will be given a full score, no extra point will be awarded for tail-recursive solutions. - - - diff --git a/dryrun/e5.md b/dryrun/e5.md deleted file mode 100644 index ddb627491a1845e316397c4b89ca63caacda95db..0000000000000000000000000000000000000000 --- a/dryrun/e5.md +++ /dev/null @@ -1,52 +0,0 @@ -# Setup - -You can use the following commands to make a fresh clone of your repository: - -``` -git clone -b e5 git@gitlab.epfl.ch:lamp/students-repositories-fall-2020/cs210-GASPAR.git cs210-e5 -cd cs210-e5 -``` - -# Be functional! - -This course is about **functional** programming, therefore you're not allowed to use the following -constructs in this assignment: -- `var` -- `while` -- `return` -- Any class in the `scala.collection.mutable` package - -# Exercise 5: FlatMap - -Our new Java intern has written some imperative code. We would prefer a purely functional solution using pattern matching. Your task is to re-implement the intern code using functional programming. - -```scala -def flatMap[T](list: List[T], f: T => List[T]): List[T] = { - var in = list - var out: List[T] = Nil - while (in.nonEmpty) { - out = out ::: f(in.head) - in = in.tail - } - out -} -``` -This solution is undesirable for several reasons: - - - it uses `var`-s and a `while` loop so it is not in the spirit of functional programming - - - it has worse than linear complexity in the output list because of the expression `out ::: f(in.head)` - -Write a new implementation of the `flatMap` method that produces the same result but satisfies the following properties: - - 1. no imperative constructs such as `var` and `while` - - 2. use pattern matching instead of methods such as `nonEmpty`, `head`, `tail` - - 3. it may define and implement additional methods - - 4. any recursive method you implement must be tail-recursive - - 5. it may only use the following methods on `List`: `::` and `:::`. Pattern matches are also allowed. - -Note that these properties are not checked by the provided automated tests and will be reviewed manually. diff --git a/dryrun/e6.md b/dryrun/e6.md deleted file mode 100644 index 7918a88a47e977e471f9e56133c4a6092c6bd2ba..0000000000000000000000000000000000000000 --- a/dryrun/e6.md +++ /dev/null @@ -1,99 +0,0 @@ -# Setup - -You can use the following commands to make a fresh clone of your repository: - -``` -git clone -b e6 git@gitlab.epfl.ch:lamp/students-repositories-fall-2020/cs210-GASPAR.git cs210-e6 -cd cs210-e6 -``` - -# Be functional! - -This course is about **functional** programming, therefore you're not allowed to use the following -constructs in this assignment: -- `var` -- `while` -- `return` -- Any class in the `scala.collection.mutable` package - -# Exercise 6: Streams - -We all know the common use case of replacing all occurrences of a substring `pattern` within a string `text` by another substring `replacement`. -If we represent strings as `List[Char]`, we can write the signature of such a method as - -```scala -def replaceAll(text: List[Char], pattern: List[Char], - replacement: List[Char]): List[Char] = /* omitted */ -``` - -Even though this method seems very well defined on its own, there are several interpretations that we can give to partially overlapping patterns and replacements. -For example, what should - -```scala -replaceAll(List('a', 'a', 'a'), List('a', 'a'), List('b')) -``` - -return? -It could return either `List('b', 'a')` or `List('a', 'b')`. - -In this exercise, we use the interpretation were patterns are tested *left-to-right*, so that the left-most match wins. -Under that interpretation, `List('b', 'a')` must be returned. - -Moreover, we choose a slightly unusual interpretation that, after replacing a substring, we consider the replacement itself, together with the rest of the input string, as candidate for further replacement. -This means that - -```scala -replaceAll(List('a', 'a', 'b', 'a', 'b', 'a'), List('a', 'b', 'a'), List('b', 'a')) -``` - -results in `List('a', 'b', 'b', 'a')`. - -Indeed, we first find the substring `aba` at index 1. -We immediately send the left part of the input (until index 1, i.e., `a`) to the output, so it will not be reconsidered for replacement. -We then replace `aba` by `ba`, resulting in a new remaining input `baba`. -In that substring, we identify a new occurrence of `aba` (after the first `b`, which is emitted to the output) which must be replaced. -Note that this subsequence was not part of the original input. -This results in the new remaining input `ba`. -At this point, no new replacement is possible. -The final result is therefore `a` followed by `b` followed by `ba`, i.e., `abba`. - - -In this exercise, the `replacement` is always guaranteed to be strictly shorter than the `pattern` (i.e., `replacement.size < pattern.size`). - -We can generalize this problem to lazy lists, by admitting `input: LazyList[Char]` and returning an output `LazyList[Char]`: - -```scala -def replaceAll(input: LazyList[Char], pattern: List[Char], - replacement: List[Char]): LazyList[Char] = ??? -``` - -Note that the `pattern` and `replacement` remain `List`s. -The resulting stream will be infinite if and only if `input` is infinite. - -## Helper function - -Implement the helper function - -``` -def testStartsWith(input: LazyList[Char], pattern: List[Char]): Option[LazyList[Char]] -``` - -which tests whether `input` starts with `pattern`, and if yes, returns the remaining of the `input`. - -Examples: - -``` -testStartsWith(LazyList('a', 'b', 'c'), List('a', 'b')) == Some(LazyList('c')) -testStartsWith(LazyList('a', 'b', 'c'), List('b', 'c')) == None -testStartsWith('a' #:: 'b' #:: infiniteLazyList, List('a')) == Some('b' #:: infiniteLazyList) -``` - -When `replacement.size >= pattern.size`, your implementation can behave in an arbitrary way (e.g., it could throw or infinitely loop). -For all other inputs, `testStartsWith` must terminate in finite time, whether `input` is finite or infinite. - -## replaceAll - -Implement the `replaceAll` function for streams. - -Your implementation must be able to handle both finite and infinite input streams. -This means that `replaceAll(input, pat, repl).take(n).toList` must complete in finite time for all `input`, `pat`, `repl` and `n`, such that `replacement.size < pattern.size`, even if `input` is infinite. diff --git a/dryrun/e7.md b/dryrun/e7.md deleted file mode 100644 index f222a5c023ff29aabd50fc32a737b45555497737..0000000000000000000000000000000000000000 --- a/dryrun/e7.md +++ /dev/null @@ -1,106 +0,0 @@ -# Setup - -You can use the following commands to make a fresh clone of your repository: - -``` -git clone -b e7 git@gitlab.epfl.ch:lamp/students-repositories-fall-2020/cs210-GASPAR.git cs210-e7 -cd cs210-e7 -``` - -# Be functional! - -This course is about **functional** programming, therefore you're not allowed to use the following -constructs in this assignment: -- `var` -- `while` -- `return` -- Any class in the `scala.collection.mutable` package - -# Exercise 7: Interpreter - -Your task in this exercise is to extend the interpreter presented in the lab to support by-name arguments. We start with an example that shows how by-name arguments work in Scala. Consider the `List.fill` function: - -```scala -object List { - // Produces a collection containing the results of some element computation a number of times. - def fill[A](n: Int)(elem: => A): List[A] = - if n != 0 then elem :: fill(n - 1)(elem) - else Nil -} -``` - -Notice that `elem` is passed by name. As a result, in `List.fill(n)(expr)`, `expr` is evaluated `n` times. For example, we can construct `List(1, 2, 3 ,4)` with a `var`: - -```scala -var count = 0 -List.fill(4)({ count += 1; count }) // List(1, 2, 3, 4) -``` - -In Scala, this is implemented by transforming function with by-name arguments, and calls to these functions. The above definition of `fill` will be transformed as follows: - -```scala -def fill[A](n: Int)(elem: () => A): List[A] = - if n != 0 then elem() :: fill(n - 1)(() => elem()) - else Nil -``` - -Likewise, usages of `fill` are transformed as follows: `List.fill(4)(()` `=> { count += 1; count })`. - -More precisely: - -- The type of by-name arguments become nullary-functions (functions with zero arguments); `elem: => A` becomes `elem: () => A` - -- In the body of by-name function definitions, references to by-name arguments become nullary-function application; `elem` becomes `elem()` - -- At call site, arguments to by-name functions are wrapped into a nullary-function; `List.fill(n)(expr)` becomes `List.fill(n)(() => expr)` - -To add by-name to our `Expr` language, we extend the enum for `Expr` with two additional constructs, `FunByName` and `CallByName`: - -```scala -enum Expr { - case Constant(value: Int) - case Name(name: String) - case BinOp(op: BinOps, arg1: Expr, arg2: Expr) - case IfNonzero(cond: Expr, caseTrue: Expr, caseFalse: Expr) - case Call(function: Expr, arg: Expr) - case Fun(arg: String, body: Expr) - // Added for this exercise: - case FunByName(param: String, body: Expr) - case CallByName(function: Expr, arg: Expr) -} -``` - -`FunByName` is a function definition with a single by-name argument. `CallByName` is call to a function taking a by name argument. As an example, assuming we had extended `Expr` to also have `Cons` and `Empty`, `fill` would be encoded as follows: - -```scala -// def fill[A](n: Int)(elem: => A): List[A] = -// if n != 0 then elem :: fill(n - 1)(elem) -// else Nil -Fun("n", FunByName("elem", - IfNonzero( - Name("n"), - Cons( - Name("elem"), - CallByName( - Call( - Name("fill"), - BinOp(BinOps.Minus, Name("n"), Constant(1)) - ), - Name("elem") - ) - ), - Empty - ) -)) -``` - -Your implementation will be done using desugaring. This means that expressions containing `FunByName` and `CallByName` will be replaced by equivalent expressions without those two constructs. Therefore, expressions that come out of desugaring should not contain any `FunByName` or `CallByName`. As a result, the implementation of `eval`, `subst` and `alphaConvert` don't need to be updated! - -We provide you with a skeleton implementation of `desugar` that traverses expressions and applies the desugaring at every step. Your task is to complete the implementation of `desugar` to transform `FunByName` and `CallByName` into semantically equivalent expressions using `Call` and `Fun`. - -Similarly to by-name arguments in Scala, your desugaring should rewrite by-name arguments according to the following scheme: - -- In the body of by-name function definitions, references to by-name arguments become nullary-function applications -- At call site, arguments to by-name functions are wrapped into a nullary-function - -Note that since in the `Expr` language all functions have exactly one argument, you need to emulate nullary-functions using dummy parameters. For instance an nullary-function definition can be emulated with `Fun("unused", expr)` where `unused` is unused in `expr`. Nullary-function application can be emulated by passing a dummy parameters, such as `Call(Name("f"), Constant(0))`. diff --git a/dryrun/e8.md b/dryrun/e8.md deleted file mode 100644 index 02f3d1ae782e2062bfc6cbaccadaf6302b84ab47..0000000000000000000000000000000000000000 --- a/dryrun/e8.md +++ /dev/null @@ -1,23 +0,0 @@ -# Setup - -You can use the following commands to make a fresh clone of your repository: - -``` -git clone -b e8 git@gitlab.epfl.ch:lamp/students-repositories-fall-2020/cs210-GASPAR.git cs210-e8 -cd cs210-e8 -``` - -# Be functional! - -This course is about **functional** programming, therefore you're not allowed to use the following -constructs in this assignment: -- `var` -- `while` -- `return` -- Any class in the `scala.collection.mutable` package - -# Exercise 8: lazy lists - -Implement a function that given a LazyList of BigDecimal returns a LazyList of averages of all data streamed so-far. - -Example: given a LazyList `(1, 2, 3, 4, 5)` you should return `(1, 1.5, 2, 2.5, 3)`. diff --git a/dryrun/first-questions.pdf b/dryrun/first-questions.pdf deleted file mode 100644 index 956271cc7475b2feea8ef24f1a81e643b31d6867..0000000000000000000000000000000000000000 Binary files a/dryrun/first-questions.pdf and /dev/null differ diff --git a/dryrun/images/1.png b/dryrun/images/1.png deleted file mode 100644 index 23fcaa31eef59c58055ec2f705eeae474768f7cc..0000000000000000000000000000000000000000 Binary files a/dryrun/images/1.png and /dev/null differ diff --git a/dryrun/images/2.png b/dryrun/images/2.png deleted file mode 100644 index 4df4380c5eab94182ecfc7d2c9097d264be01baa..0000000000000000000000000000000000000000 Binary files a/dryrun/images/2.png and /dev/null differ diff --git a/dryrun/images/3.png b/dryrun/images/3.png deleted file mode 100644 index cc9b3514749a38de69e7760681054e1b2570b683..0000000000000000000000000000000000000000 Binary files a/dryrun/images/3.png and /dev/null differ diff --git a/dryrun/images/4.png b/dryrun/images/4.png deleted file mode 100644 index 7b230493dee2cdc34440edb32a77b64702164ba7..0000000000000000000000000000000000000000 Binary files a/dryrun/images/4.png and /dev/null differ diff --git a/exercises/.gitkeep b/exercises/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/exercises/Group workspaces.md b/exercises/Group workspaces.md deleted file mode 100644 index 608b01eed5d617c1df5500444c3986fc3999a84d..0000000000000000000000000000000000000000 --- a/exercises/Group workspaces.md +++ /dev/null @@ -1,15 +0,0 @@ -Choose a group that has the same letter as your on-site rotation group ([epfl-covid19-studies](https://www.epfl.ch/campus/security-safety/en/health/coronavirus-covid19/students/)). - -In case the on-site rotation group is too unbalanced and all groups get filled, please contact nicolas.stucki@epfl.ch to open new groups. - -It is allowed but not recommended to join a group in another rotation as long as all the work is done remotely with your group (using Discord, Hangouts, Zoom, ...). - -You can find the link to the workspace of your group in the form. -If you work remotely you can just write down the solution directly in the workspace. -If you solved it on paper or on a blackboard/whiteboard you can take a picture with your phone and paste it in the workspace. - -This form is also used to initialize your programming assignments using your gaspar. - -You should not change groups during the semester as we wont be tracking changes to the form - -[**Choose your group form**](https://docs.google.com/spreadsheets/d/1xMy83u1bl1yd4zuYt9jFp7pEYGurTSlfK8onmfOnVyA/edit#gid=0) diff --git a/exercises/exercise-1.md b/exercises/exercise-1.md deleted file mode 100644 index 542ad8f645704844b12953674bc50addcb56e6d3..0000000000000000000000000000000000000000 --- a/exercises/exercise-1.md +++ /dev/null @@ -1,59 +0,0 @@ -# Exercise Session 1 - -We will work on tail recursion in this session. - -## Question 1: Factorial - -Recall the factorial function that you saw in class - -```scala -def factorial(n: Int): Int = if (n <= 0) 1 else n * factorial(n - 1) -``` - -Define a tail recursive version of it - -```scala -def factorial(n: Int): Int = fact(n, 1) -@tailrec -def fact(n: Int, acc: Int): Int = ??? -``` - -What would be the advantage of making `fact` an inner function to `factorial`? - -## Question 2: Sum of elements on a list - -Define a function that takes a list of integers and sums them. You can use the functions `head`, `tail`, and `isEmpty` on lists, as you have seen for your homework. - -```scala -def sumList(ls: List[Int]): Int = ??? -``` - -Convert your definition into a tail-recursive one. - -## Question 3: Fast exponentiation - -Fast exponentiation is a technique to optimize the exponentiation of numbers: - -``` -b²ⁿ = (b²)ⁿ = (bⁿ)² -b²ⁿ⁺¹ = b * b²ⁿ -``` - -Define a function that implements this fast exponentiation. Can you define a tail recursive version as well? - -```scala -def fastExp(base: Int, exp: Int): Int = ??? -``` - -## Question 4: Tail recursive Fibonacci - -Define a function that computes the nth Fibonacci number. Can you define a tail recursive version as well? The Fibonacci recurrence is given as follows: - -``` -fib(n) = 1 | n = 0, 1 -fib(n) = fib(n - 1) + fib(n - 2) | otherwise -``` - -```scala -def fibonacci(n: Int): Int = ??? -``` diff --git a/exercises/exercise-10.md b/exercises/exercise-10.md deleted file mode 100644 index e09628996509cd5182393d0d970020370e0a86c6..0000000000000000000000000000000000000000 --- a/exercises/exercise-10.md +++ /dev/null @@ -1,61 +0,0 @@ -# Exercise Session 10 - -In these exercises, you are asked to write higher-order functions in the simple untyped language supported by the interpreter for recursive higher-order functions that we developed in the lectures. - -## Question 1 - -In this exercise, you will be working with _Church numerals_. -Church numerals are a representation of natural numbers using only functions. -In this encoding, a number `n` is represented by a function that maps any function `f` to its `n`-fold composition. -For example, `0`, `1`, `2` and `3` are represented as follows: - -```scala -def zero = (f => x => x) -def one = (f => x => f x) -def two = (f => x => f (f x)) -def three = (f => x => f (f (f x))) -``` - -Give an implementation of the `succ` function that takes a Church numeral and returns its successor. -For example, `(succ zero)` evaluates to the definition of `one` and `(succ one)` evaluates to the definition of `two`. - -## Question 2 - -Give an implementation of the `add` function that takes two Church numerals and returns their sum, using `succ`. - -## Question 3 - -In this exercise, you will be working with _Church-encoded lists_. -Much like Church numerals, Church-encoded lists are a representation of lists using only functions. -In this encoding, a list is represented by a function that takes two arguments and -returns the first one if the list is empty -or returns the second one applied to head and tail if the list is non-empty: - -```scala -def nil = (n => c => n) -def cons = (h => t => - n => c => c h t) -``` - -For example, `List(1,2,3)` would be represented in this encoding as follows: - -```scala -(cons 1 (cons 2 (cons 3 nil))) -``` - -With Church-encoded lists, decomposition is achieved by "applying" the list to a pair of continuations, one for the empty case and another one for the non-empty case. For example, concatenation of two Church-encoded lists could be implemented as follows: - -```scala -def cat = (l1 => l2 => - (l1 l2 (h => t => (cons h (cat t l2)))) -``` - -Give an implementation of the `size` function that takes a Church-encoded list and returns its size as a Church numeral. You are allowed to use the `succ` function defined earlier. - -## Question 4 - -Give an implementation of the `map` function which takes a Church-encoded list and a function and returns the list mapped with the function (in the sense of `List.map`). You may use recursion in your definitions. - -## Question 5 - -Give an implementation of the `foldRight` function which takes a Church-encoded list and a function and returns the result of `foldRight`. You may use recursion in your definitions. diff --git a/exercises/exercise-2.md b/exercises/exercise-2.md deleted file mode 100644 index 52ac8650e80ab723966e90856472d59df50ec53f..0000000000000000000000000000000000000000 --- a/exercises/exercise-2.md +++ /dev/null @@ -1,109 +0,0 @@ -# Exercise Session 2 - -This week we will work on playing with functions as values. - -## Question 1. - -Define the function `flip`. It takes a function and returns the same function, but with the arguments flipped. - -```scala -def flip(f: (Int, Double) => Int): (Double, Int) => Int = ??? -``` - -## Question 2.1 - -Define the identity function for integers, which, given an `Int`, returns it - -```scala -val id: Int => Int = ??? -``` - -## Question 2.2 - -Define the compose function, that, given 2 functions `f`, `g`, returns a function that composes them, i.e., `f ∘ g`. - -```scala -def compose(f: Int => Int, g: Int => Int): Int => Int = ??? -``` - -What does `compose(id, f)(k)` evaluate to, for some function `f` and integer `k`? - -## Question 2.3 - -Define the function `repeated`, which takes a function and repeatedly applies it `n` times (`n ≥ 0`). - -```scala -def repeated(f: Int => Int, n: Int): Int => Int = ??? -``` - -_Hint_: What values should be returned by `repeated(x => x + 1, 0)` and `repeated(x => x + 1, 3)`? - -## Question 3.1 - -Define the function `curry2`, that curries a two arguments function. That is, `curry2(f) = g` such that `f(x, y) == (g(x))(y)` - -```scala -def curry2(f: (Double, Int) => Boolean): Double => (Int => Boolean) = ??? -``` - -_Hint_: what should `curry2((x, y) => x < y)(1.0)` return? - - -## Question 3.2 - -Define the function `uncurry2`. It takes a curried function, and creates a two-argument function. - -```scala -def uncurry2(f: Double => Int => Boolean): (Double, Int) => Boolean = ??? -``` - -## Question 4. - -Write a function `fixedPoint` with the following signature: - -```scala -def fixedPoint(f: Int => Int): Int => Int -``` - -The function takes a function `f` and returns a function that maps an integer into the fixed point of f that is obtained by iterating `f` some finite number of times starting from the initial value. - -A value `x` is a fixed point of `f` if `f(x) == x`. - -For each of the following expressions, indicate whether it terminates, and if so, what is the value returned: - -- `fixedPoint(x => x/2)(4)` -- `fixedPoint(id)(123456)` -- `fixedPoint(x => x + 1)(0)` -- `fixedPoint(x => if (x % 10 == 0) x else x + 1)(35)` -- `fixedPoint((x: Int) => x / 2 + 5)(20)` - -## Question 5.1 - -Write the `sum` function with the following signature: - -```scala -def sum(a: Int, b: Int)(f: Int => Int): Int = ??? -``` - -Which returns the sum of `f(i)` where `i` ranges from `a` to `b`. - -_Bonus point_: Can your implementation be tail recursive ? - -## Question 5.2 - -Write the `quadratic` function with the following signature: - -```scala -def quadratic(c: Int): Int => Int = ??? -``` - -Which returns a function that takes an integer `x` as argument and returns `(x - c)²`. - -## Question 5.3 - -Using the above functions, define the function `quad3Integrate` which, given two integers `a` and `b`, computes the sum of `(i - 3)²` where `i` ranges from `a` to `b - 1`. - -```scala -def quad3Integrate(a: Int, b: Int): Int = ??? -val quad3Integrate: (Int, Int) => Int = ??? -``` diff --git a/exercises/exercise-3.md b/exercises/exercise-3.md deleted file mode 100644 index 7d7ca3033b5f78d9d35bdcf410cbab70ed7163ce..0000000000000000000000000000000000000000 --- a/exercises/exercise-3.md +++ /dev/null @@ -1,80 +0,0 @@ -# Exercise Session 3 - -This week we will play with genericity and OO concepts. - -A binary search tree is a binary tree such that, for a node, all elements in the left sub-tree are smaller than the element at the node, and all elements in the right sub-tree are greater than the element at the node. Therefore, binary search trees do not contain duplicate elements. - -Because we want to build a generic tree structure, we also need the notion of a comparator, or a less-than-or-equal operator (denoted `leq`) for two generic elements which satisfies the following properties: - -- Transitivity: `leq(a, b) && leq(b, c) => leq(a, c)` -- Reflexivity: `leq(a, a)` is `true`. -- Anti-symmetry: `leq(a, b) && leq(b, a) => a == b` -- Totality: either `leq(a, b)` or `leq(b, a)` is `true` (or both) - -Note that the above defines a total order. - -Here is the structure we will be using for implementing these trees: - -```scala -trait Tree[T] { ... } -case class EmptyTree[T](leq: (T, T) => Boolean) extends Tree[T] { ... } -case class Node[T](left: Tree[T], elem: T, right: Tree[T], leq: (T, T) => Boolean) extends Tree[T] { ... } -``` - -For consistency, all subtrees must contain the same leq parameter. -Creating an empty binary tree for integers can be then done as follows: - -```scala -val intLeq: (Int, Int) => Boolean = (x, y) => x <= y -val emptyIntTree: Tree[Int] = new EmptyTree(intLeq) -``` - -## Question 1 - -Given only `leq` for comparison, how can you test for equality? How about strictly-less-than? - -## Question 2 - -Define the size method on `Tree[T]`, which returns its size, i.e. the number of Nodes in the tree. - -```scala -trait Tree[T] { - def size: Int - ... -} -``` - -Implement it in two ways: - -1. within `Tree[T]`, using pattern matching, -2. in the subclasses of `Tree[T]`. - -## Question 3 - -Define the `add` method, that adds an element to a `Tree[T]`, and returns the resulting tree: - -```scala -def add(t: T): Tree[T] = ??? -``` - -Remember that trees do not have duplicate values. If t is already in the tree, the result should be unchanged. - -## Question 4 - -Define the function `toList`, which returns the sorted list representation for a tree. For example, `emptyIntTree.add(2).add(1).add(3).toList` should return `List(1, 2, 3)` - -```scala -def toList: List[T] = ??? -``` - -You can use the `Nil` operator for creating an empty list, and the `::` operator for adding a new element to the head of a list: `1 :: List(2, 3) == List(1, 2, 3)`. You are naturally free to define any auxiliary functions as necessary. - -## Question 5 - -Define the function `sortedList`, which takes an unsorted list where no two elements are equal, and returns a new list that contains all the elements of the previous list (and only those), in increasing order. - -```scala -def sortedList[T](leq: (T, T) => Boolean, ls: List[T]): List[T] = ??? -``` - -_Hint_: you might need to define some auxiliary functions. diff --git a/exercises/exercise-4.md b/exercises/exercise-4.md deleted file mode 100644 index 578a00b2f33804b377604afaa6095839e00b5ada..0000000000000000000000000000000000000000 --- a/exercises/exercise-4.md +++ /dev/null @@ -1,71 +0,0 @@ -# Exercise Session 4 - -Variance and Pattern Matching - -This week, we will work on the idea of variance, and on pattern matching. Recall that - -- Lists are covariant in their only type parameter. -- Functions are contravariant in the argument, and covariant in the result. - -## Question 1 - -Consider the following hierarchies: - -```scala -abstract class Fruit -class Banana extends Fruit -class Apple extends Fruit -abstract class Liquid -class Juice extends Liquid -``` - -Consider also the following typing relationships for `A`, `B`, `C`, `D`: `A <: B` and `C <: D`. - -Fill in the subtyping relation between the types below. Bear in mind that it might be that neither type is a subtype of the other. - -| Left hand side | ?: | Right hand side | -| ---: | --- | :--- | -| List[Banana] | | List[Fruit] | -| List[A] | | List[B] | -| Banana => Juice | | Fruit => Juice | -| Banana => Juice | | Banana => Liquid | -| A => C | | B => D | -| List[Banana => Liquid] | | List[Fruit => Juice] | -| List[A => D] | | List[B => C] | -| (Fruit => Juice) => Liquid | | (Banana => Liquid) => Liquid | -| (B => C) => D | | (A => D) => D | -| Fruit => (Juice => Liquid) | | Banana => (Liquid => Liquid) | -| B => (C => D) | | A => (D => D) | - -## Question 2 - -The following data types represent simple arithmetic expressions: - -```scala -abstract class Expr -case class Number(x: Int) extends Expr -case class Var(name: String) extends Expr -case class Sum(e1: Expr, e2: Expr) extends Expr -case class Prod(e1: Expr, e2: Expr) extends Expr -``` - -Define a function `deriv(expr: Expr, v: String): Expr` returning the expression that is the partial derivative of `expr` with respect to the variable `v`. - -```scala -def deriv(expr: Expr, v: String): Expr = ??? -``` - -Here's an example run of the function: - -```scala -> deriv(Sum(Prod(Var("x"), Var("x")), Var("y")), "x") -Sum(Sum(Prod(Var("x"), Number(1)), Prod(Number(1), Var("x"))), Number(0)) -``` - -## Question 3 - -Write an expression simplifier that applies some arithmetic simplifications to an expression. For example, it would turn the above monstrous result into the following expression: - -```scala -Prod(Var("x"), Number(2)) -``` diff --git a/exercises/exercise-5.md b/exercises/exercise-5.md deleted file mode 100644 index 4bd41495efec2e3845de92caf630c919bd0632af..0000000000000000000000000000000000000000 --- a/exercises/exercise-5.md +++ /dev/null @@ -1,73 +0,0 @@ -# Exercise Session 5 - -*Structural Induction* - -## Question 1 - -Prove that the following equivalence holds by using inductive reasoning: - -``` -alist map f map g === alist map (f andThen g) -``` - -Axioms: - - 1) `Nil map f === Nil` - 2) `(x :: xs) map f === f(x) :: (xs map f)` - 3) `(f andThen g)(x) === g(f(x))` - -**Note**: Be very precise in your proof: - -- Clearly state which axiom you use at each step, and when/if you use the induction hypothesis. -- Use only 1 axiom/hypothesis at each step, and only once. Applying 2 axioms requires 2 steps. -- Underline the part of an equation on which you apply your axiom. -- Make sure to state what you want to prove, and what your induction hypothesis is, if any. - -## Question 2 - -A more complicated proof (midterm 2016) - -We want to implement a function `sum(list: List[Int]): Int`, which returns the sum of the elements of a list of `Int`-s. We can easily *specify* that function as follows: - -``` -(1) sum(Nil) === 0 -(2) sum(x :: xs) === x + sum(xs) -``` - -If we naively translate this specification into a Scala implementation, we end up with a uselessly non-tail recursive function. Besides, doing the recursion ourselves is wasteful. Instead, we implement it using `foldLeft`: - -```scala -def betterSum(list: List[Int]): Int = - list.foldLeft(0)(add) - -def add(a: Int, b: Int): Int = a + b -``` - -However, that implementation is not trivially correct anymore. We would like to *prove* that it is correct for all lists of integers. In other words, we want to prove that - -``` -list.foldLeft(0)(add) === sum(list) -``` - -for all lists of integers. - -In addition to the specification of `sum` `(1-2)`, you may use the following axioms: - -``` -(3) Nil.foldLeft(z)(f) === z -(4) (x :: xs).foldLeft(z)(f) === xs.foldLeft(f(z, x))(f) -(5) add(a, b) === a + b -(6) a + b === b + a -(7) (a + b) + c === a + (b + c) -(8) a + 0 === a -``` - -Axioms 3-5 follow from the implementations of `foldLeft` and `add`. Axioms 6-8 encode well-known properties of `Int.+`: commutativity, associativity, and neutral element. - -**Your task**: prove the following lemma by structural induction: - -```scala -list.foldLeft(z)(add) === z + sum(list) -``` - -From that lemma, we can "trivially" (with the help of axioms 6 and 8) derive that the implementation of `betterSum` is correct by substituting `0` for `z` in the lemma. You are not asked to do that last bit. diff --git a/exercises/exercise-6.md b/exercises/exercise-6.md deleted file mode 100644 index b805f493e4ef82766faef69f51106c34aa9a0213..0000000000000000000000000000000000000000 --- a/exercises/exercise-6.md +++ /dev/null @@ -1,83 +0,0 @@ -# Exercise Session 6 - -For comprehensions and monads - -## Question 1.1 - -Consider a directed graph given by its set of (directed) edges stored as a list of pairs of nodes: - -```scala -type NodeId = Int -type DirectedEdge = (NodeId, NodeId) -type DirectedGraph = List[DirectedEdge] -``` - -Define, non-recursively, the `triangles` function that finds all cycles of length 3, with three distinct nodes, in the given graph. You should use a for comprehension. - -```scala -def triangles(edges: DirectedGraph): List[(NodeId, NodeId, NodeId)] = for ... -``` - -Each cycle should appear only once. For instance, given the edges: - -``` -List((1, 2), (2, 3), (3, 1)) -``` - -You should return exactly one of the three following possibilities: - -``` -(1, 2, 3), (2, 3, 1), (3, 1, 2) -``` - -You are free to decide which of the three you return. - - -## Question 1.2 - -After that, translate the for comprehension you wrote in the appropriate combination of `map`/`flatMap`/`filter` calls. - - -## Question 2. - -Monads are often defined with a `map` method in addition to `flatMap`: - -```scala -trait M[T] { - def flatMap[U](f: T => M[U]): M[U] - def map[U](f: T => U): M[U] -} -``` - -Where `map` and `flatMap` are related by the following law: - -``` -Monad/Functor Consistency: -m.map(f) === m.flatMap(x => unit(f(x))) -``` - -We introduce you a new algebraic structure called `Functor`-s. We say that a `type F` is a `Functor` if `F[T]` has a `map` method with the following signature: - -```scala -trait F[T] { - def map[U](f: T => U): F[U] -} -``` - -And there is a `unit` method for `F` with the following signature: - -```scala -def unit[T](x: T): F[T] -``` - -Such that `map` and `unit` fulfill the following laws: - -``` -Identity: -m.map(x => x) === m - -Associativity: -m.map(h).map(g) === m.map(x => g(h(x))) -``` - -Prove that any `Monad` with a `map` method that fulfills the `Monad/Functor Consistency` law is also a `Functor`. diff --git a/exercises/exercise-7.md b/exercises/exercise-7.md deleted file mode 100644 index 5ab1f58785c5a5be4d95ea39a8e0f88f4e65243f..0000000000000000000000000000000000000000 --- a/exercises/exercise-7.md +++ /dev/null @@ -1,62 +0,0 @@ -# Exercise Session 7 - -## Question 1 - -Consider the following series: - -``` -1 -1 1 -2 1 -1 2 1 1 -1 1 1 2 2 1 -3 1 2 2 1 1 -........... -``` - -1. Find the next element in the sequence above. - -Now, let us encode an element of the sequence above as a `List[Int]`. - - -2. Write a function to compute the next element. - -```scala -def nextLine(currentLine: List[Int]): List[Int] = ??? -``` - -3. Implement a lazy list `funSeq` which constructs this sequence. Recall: to construct a lazy list, you can use `LazyList.cons[A](a: A, b: => LazyList[A]): LazyList[A]` - -```scala -lazy val funSeq: LazyList[List[Int]] = ... -``` - -## Question 2 - -1. Write a lazy list of squares of integers ≥ 1. You may use `LazyList.from(i: Int)` - -```scala -val squares: LazyList[Int] = ... -``` - -2. Write a lazy list of all non-empty strings using the characters "0" and "1" and the concatenation operation +. In other words, every non-empty string composed of "0" and "1" should be reached at some point. - -```scala -val codes: LazyList[String] = ... -``` - -3) Using `codes`, write a lazy list of all possible non-empty palindromes of "0" and "1". You may use the `.reverse` function defined on strings. - -```scala -val palCodes: LazyList[String] = ... -``` - -4. Can you do the same without filtering? The palindromes need not to be in the same order. - -5. Given another lazy list `otherCodes`, possibly finite or infinite, you don't know at first: - -```scala -val otherCodes: LazyList[String] = [some external source] -``` - -Build a lazy list `allCodes` that interleaves `palCodes` and `otherCodes`. diff --git a/exercises/exercise-8.md b/exercises/exercise-8.md deleted file mode 100644 index 3d6ce6fa5f676726e08efaea9af5d86757e99cc9..0000000000000000000000000000000000000000 --- a/exercises/exercise-8.md +++ /dev/null @@ -1,130 +0,0 @@ -# Exercise Session 8 - -## Question 1 - -In this exercise, we will define instances of the `Eq` type class. -Recall the `Ordering[A]` type class introduced in the lecture. -An instance of `Ordering[A]` allows us to compare values of type `A` -to see which one (if any) is smaller. -Similarly, an instance of `Eq[A]` allows us to compare values of type `A` to see if they are equal. -We can define it as follows: - -```scala -trait Eq[T]: - extension (x: T) - def === (y: T): Boolean -``` - -1. Write a `given` instance to create `Eq[List[T]]` from a `Eq[T]`. -2. Write a `given` instance to create `Eq[(T, U, S)]` from `Eq[T]`, `Eq[U]` and `Eq[S]`. -3. Write `given` instance to create `Eq[Person]`. Make use of both the definitions you have written previously. - -```scala -case class Person(name: String, age: Int, neighbours: List[String]) -``` - -4. Explicitly write the `using` argument to `summon` (you may need to assign names to your `given` definitions): - -```scala -summon[Eq[Person]](using ...) -``` - -## Question 2 - -In this exercise, we will use term inference to calculate types based on other types. -First, we define `Nat`: - -```scala -trait Nat - -/** Zero */ -case object Z extends Nat -type Z = Z.type - -/** The Successor of N */ -case class S[N <: Nat](pred: N) extends Nat -``` - -`Nat` encodes natural numbers on the type level: - -```scala -S(S(Z)) : S[S[Z]] -``` - -The type `S[S[Z]]` represents the successor of the successor of zero: two. - -We can add two values of type `Nat` as follows: - -```scala -def add1(n: Nat, m: Nat): Nat = - n match - case Z => m - case S(n1) => S(add1(n1, m)) -``` - -However, this definition has an issue: the result is imprecisely typed. -To fix this, we will define the `Sum[N, M, R]` type class. -An instance of Sum represents evidence that `N + M = R`. -With the appropriate given definitions, the compiler can infer an instance of `Sum[N, M, R]` such that `N + M = R`, for any pair of natural numbers `N` and `M`: - -```scala -case class Sum[N <: Nat, M <: Nat, R <: Nat](result: R) - -given zero as Z = Z -given succ[N <: Nat](using n: N) as S[N] = S(n) - -given sumZ[N <: Nat](using n: N) as Sum[Z, N, N] = Sum(n) - -given sumS[N <: Nat, M <: Nat, R <: Nat]( - using sum: Sum[N, M, R] -) as Sum[S[N], M, S[R]] = Sum(S(sum.result)) -``` - -Note how the last two `given` definitions reflect the definition of the `add1` method: `sumZ` corresponds to the case for `Z + M`, and `sumS` corresponds to the case for `S[N] + M`. -The second case is recursive, explicitly for `add1` and using term inference for the `given` definition. - -Using the above definitions, we can write a function that adds two `Nat` -values and assigns a precise type to the result: - -```scala -def add[N <: Nat, M <: Nat, R <: Nat](n: N, m: M)( - using sum: Sum[N, M, R] -): R = sum.result -``` - -As an example, the result of adding `S[Z]` to `S[Z]` is `S[S[Z]]`: - -```scala -sum(S(Z), S(Z)) : S[S[Z]] -``` - -1. Write explicitly the `using` argument to `sum` (it may help to write all type parameters explicitly): - -```scala -add(S(Z), S(S(Z)))(using ...) -``` - -*Note:* If you try this in the Scala 3 compiler right now, the result won't be correct, -this is a known compiler bug: https://github.com/lampepfl/dotty/issues/7586 - - -2. Write `given` definitions that create an instance of the -`Product[N, M, R]` type class, representing the evidence that `N * M = R`. - -```scala -case class Product[N <: Nat, M <: Nat, R <: Nat](result: R) -``` - -Hint 1: Multiplying two natural numbers is defined as follows: - -```scala -def mul1(n: Nat, m: Nat): Nat = - n match - case Z => Z - case S(n1) => add(m, mul1(n1, m)) -``` - -Hint 2: The `R` type parameter is key to making the `add` definition work. -When we start inferring a term of type `Sum[N, M, R]`, we already know what -`N` and `M` are, but we don't know what `R` is. Inferring the term will also -infer the type that stands for `R`. diff --git a/exercises/exercise-9.md b/exercises/exercise-9.md deleted file mode 100644 index 5e00c8321c6cc7c85a4cbf405904009f05a3c28b..0000000000000000000000000000000000000000 --- a/exercises/exercise-9.md +++ /dev/null @@ -1,125 +0,0 @@ -# Exercise Session 9 - -## Question 1 - -For this exercise, you are given the following ASTs representing an expression `Expr` - -```scala -enum Expr: - case Constant(value: Int) // Numeric constants - case Name(name: String) // reference to a name - case BinOp(op: BinOps, arg1: Expr, arg2: Expr) // primitive binary operation - case IfNonzero(cond: Expr, caseTrue: Expr, caseFalse: Expr) // conditional - case Call(function: Expr, arg: Expr) // function call - case Fun(param: String, body: Expr) // function definition -end Expr -``` - -We also provide a few valid primitive operations that you may use - -```scala -def minus(e1: Expr, e2: Expr) = BinOp(Minus, e1, e2) -def plus(e1: Expr, e2: Expr) = BinOp(Plus, e1, e2) -def leq(e1: Expr, e2: Expr) = BinOp(LessEq, e1, e2) // 1 if e1 <= e2; 0 otherwise -def times(e1: Expr, e2: Expr) = BinOp(Times, e1, e2) -def modulo(e1: Expr, e2: Expr) = BinOp(Modulo, e1, e2) -``` - -The global environment is a sequence of `"name" -> definition` tuples (of type `(String, Expr)`). -All definitions can reference all names in the global environment. - -Where for example one could define a division function `div` as follows: -```scala -"div" -> Fun("x", Fun("y", - IfNonzero(BinOp(LessEq, Name("y"), Name("x")), - plus(Constant(1), - Call(Call(Name("div"), minus(Name("x"), Name("y"))), - Name("y"))), - Constant(0)))) -``` - - -Your task is to implement the greatest common divisor (`gcd`) function in `Expr`. - -```scala -"gcd" -> … // TODO implement me -``` - -Hint: in Scala the `gcd` can be implemented as -```scala -def gcd(a: Int, b: Int): Int = if b == 0 then a else gcd(b, a%b) -``` - -## Question 2 - -For this exercise, we will add lists to our language -```scala -enum Expr: - ... - case Cons(head: Expr, tail: Expr) // Cons list - case EmptyList // empty of a Cons list - // matches a list - case Match(scrutinee: Expr, caseEmpty: Expr, headName: String, tailName: String, caseCons: Expr) -end Expr -``` - -For example, the following program a match in Scala would translate as - -```scala -ls match - case Nil => Nil - case x :: xs => x :: xs -// becomes -Match(Name("ls"), - EmptyList, - "x", "xs", Cons(Name("x"), Name("xs")) -) -``` - - -### Map -Your task is to implement the `map` function in `Expr`. - -Hint: -```scala -def map(ls: List[Int])(f: Int => Int): List[Int] = ls match - case Nil => Nil - case x :: xs => f(x) :: map(xs)(f) -``` - - -### Fold Left -Your task is to implement the `foldLeft` function in `Expr`. -Hint: -```scala -def foldLeft(ls: List[Int])(acc: Int)(f: (Int, Int) => Int): Int = ls match - case Nil => acc - case x :: xs => foldLeft(xs)(f(acc, x))(f) -``` - -## Question 3 - -For this exercise, we will add writable cells to our language. Assume we have a global array of memory that can be accessed by index. -```scala -enum Expr: - ... - // read from position `idx` - case Read(idx: Expr) - // write the `value` to position `idx` and then evaluates and return the `andThen` expression - case Write(idx: Expr, value: Expr, andThen: Expr) -end Expr -``` - - -Your task is to implement the `CAS` (compare and swap) function in `Expr`. - -Hint: -```scala -val mem: Array[Int] = ??? -def CAS(idx: Int)(old: Int)(nw: Int): Int = - if mem(idx) != old then - 0 - else - mem(idx) = nw - 1 -``` diff --git a/exercises/solution-1.md b/exercises/solution-1.md deleted file mode 100644 index cb5f0555969239f15ee61777fd1aef9596112541..0000000000000000000000000000000000000000 --- a/exercises/solution-1.md +++ /dev/null @@ -1,35 +0,0 @@ -# Exercise Session 1, Solutions - -## Question 3: Fast exponentiation - -```scala -def fastExp(base: Int, exp: Int): Int = { - require(exp >= 0) - - @tailrec - def go(base: Int, exp: Int, acc: Int): Int = { - if (exp == 0) - acc - else if ((exp % 2) != 0) - go(base, exp - 1, base * acc) - else - go(base * base, exp / 2, acc) - } - go(base, exp, 1) -} -``` - -## Question 4: Tail recursive Fibonacci - -```scala -def fibonacci(n: Int): Int = { - require(n >= 0) - - @tailrec - def go(k: Int, previous: Int, current: Int): Int = - if (k == n) current - else go(k + 1, current, previous + current) - - if (n == 0) 1 else go(1, 1, 1) -} -``` diff --git a/exercises/solution-10.md b/exercises/solution-10.md deleted file mode 100644 index bf9e75ff7de47063b49a95992321bd26d9ce486a..0000000000000000000000000000000000000000 --- a/exercises/solution-10.md +++ /dev/null @@ -1,26 +0,0 @@ -# Exercise Session 10, Solutions - -## Question 1 - -```scala -def succ = (n => f => x => f(n(f)(x))) -``` - -## Question 2 - -```scala -def add = (n => m => n(succ)(m)) -``` - -## Question 3 - -```scala -def size = (l => l(zero)( h => t => succ(size(t)) )) -``` - -## Question 4 - -```scala -def map = (f => l => - l(nil)( h => t => cons( f(h) )( map(f)(t) ) )) -``` diff --git a/exercises/solution-2.md b/exercises/solution-2.md deleted file mode 100644 index 76d374e78d0b330a9e4f2a182df9a15858ec2a8e..0000000000000000000000000000000000000000 --- a/exercises/solution-2.md +++ /dev/null @@ -1,30 +0,0 @@ -# Exercise Session 2, Solutions - -## Question 3.1 - -```scala -def curry2(f: (Double, Int) => Boolean): Double => (Int => Boolean) = - (x1: Double) => (x2: Int) => f(x1, x2) -``` - -## Question 3.2 - -```scala -def uncurry2(f: Double => Int => Boolean): (Double, Int) => Boolean = - (x1: Double, x2: Int) => f(x1)(x2) -``` - -## Question 4. - -```scala -def fixedPoint(f: Int => Int): Int => Int = { - - @tailrec - def rec(x: Int): Int = { - val y = f(x) - if x == y then x else rec(y) - } - - rec -} -``` diff --git a/exercises/solution-3.md b/exercises/solution-3.md deleted file mode 100644 index e27547d2ad3a4f0fd8d44fdb164464b5052acba5..0000000000000000000000000000000000000000 --- a/exercises/solution-3.md +++ /dev/null @@ -1,28 +0,0 @@ -# Exercise Session 3, Solutions - -## Question 3 - -```scala -def toList: List[T] = { - def toListAcc(tree: Tree[T], acc: List[T]): List[T] = tree match { - case EmptyTree(_) => acc - case Node(left, elem, right, _) => - toListAcc(left, elem :: toListAcc(right, acc)) - } - - toListAcc(this, Nil) -} -``` - -## Question 4 - -```scala -def sortedList[T](leq: (T, T) => Boolean, ls: List[T]): List[T] = { - def buildTree(elems: List[T], acc: Tree[T]): Tree[T] = elems match { - case Nil => acc - case elem :: rest => buildTree(rest, acc.add(elem)) - } - - buildTree(ls, EmptyTree(leq)).toList -} -``` diff --git a/exercises/solution-4.md b/exercises/solution-4.md deleted file mode 100644 index a6ed930574f1799fb028970d420a7b5e7dae61a8..0000000000000000000000000000000000000000 --- a/exercises/solution-4.md +++ /dev/null @@ -1,64 +0,0 @@ -# Exercise Session 4, Solutions - -## Question 1 - -| Left hand side | ?: | Right hand side | -| ---: | --- | :--- | -| List[Banana] | <: | List[Fruit] | -| List[A] | <: | List[B] | -| Banana => Juice | >: | Fruit => Juice | -| Banana => Juice | <: | Banana => Liquid | -| A => C | X | B => D | -| List[Banana => Liquid] | >: | List[Fruit => Juice] | -| List[A => D] | >: | List[B => C] | -| (Fruit => Juice) => Liquid | >: | (Banana => Liquid) => Liquid | -| (B => C) => D | >: | (A => D) => D | -| Fruit => (Juice => Liquid) | X | Banana => (Liquid => Liquid) | -| B => (C => D) | X | A => (D => D) | - -## Question 2 - -```scala -def deriv(e: Expr, v: String): Expr = e match { - case Number(_) => Number(0) - case Var(name) => if (name == v) Number(1) else Number(0) - case Sum(left, right) => Sum(deriv(left, v), deriv(right, v)) - case Prod(left, right) => - Sum(Prod(deriv(left, v), right), Prod(left, deriv(right, v))) -} -``` - -## Question 3 - -```scala -def simplify(expr: Expr): Expr = expr match { - case Number(_) => - expr - case Var(_) => - expr - case Sum(a, b) => - (simplify(a), simplify(b)) match { - case (Number(x), Number(y)) => Number(x + y) - case (Number(0), y) => y - case (x, Number(0)) => x - case (x, y) => if (x == y) Prod(Number(2), x) else Sum(x, y) - } - case Prod(a, b) => - (simplify(a), simplify(b)) match { - case (Number(x), Number(y)) => Number(x * y) - case (Number(0), _) => Number(0) - case (_, Number(0)) => Number(0) - case (Number(1), y) => y - case (x, Number(1)) => x - case (x, y) => Prod(x, y) - } -} -``` - -Note that this is a pretty open question. The above solution is still incapable of simplifying `Sum(Number(1), Sum(Var("x"), Number(2))) into Sum(Number(3), Var("x"))`, which is disappointing. Can you imagine ways to improve it? - -**Idea for further exercise**: - -Implement a `simplify` function which will completely simplify all expressions given to it. - -_Hint_: Instead of trying to add more special cases in the above function, try to convert the expression into some kind of normalized form. For instance, this normalized form could be a sum of products (of numbers and variables), represented as a list of lists of expressions. Then, you could apply simplifications on this normalized form. Finally, you would have to reconstruct a single expression out of it. diff --git a/exercises/solution-5.md b/exercises/solution-5.md deleted file mode 100644 index 207bc273be2ffd03de0c52ad9ee2a34544668a8a..0000000000000000000000000000000000000000 --- a/exercises/solution-5.md +++ /dev/null @@ -1,51 +0,0 @@ -# Exercise Session 5, Solutions - -## Question 2 - -**To prove**: - -We want to prove `P(list)` for any list of type `List[Int]`, where `P(list)` is defined as: - -```scala -P(list) := list.foldLeft(z)(add) === z + sum(list), for all z of type Int -``` - -The proof proceeds by structural induction on `list`. - -**Case Nil**: - -We want to show `P(Nil)`. -Let `z` be an arbitrary expression of type `Int`. - -``` -sNil.foldLeft(z)(add) === (by 3) z - === (by 8) z + 0 - === (by 1) z + sum(Nil) -``` - -Which proves `P(Nil)`. - -**Case x :: xs**: - -We want to show `P(x :: xs)`, assuming `P(xs)`. - -Induction hypothesis: `P(xs)` - -``` -(IH) xs.foldLeft(z')(add) === z' + sum(xs), for all z' of type Int -``` - -Let `z` be an arbitrary expression of type `Int`. - -``` -(x :: xs).foldLeft(z)(add) - === (by 4) xs.foldLeft(add(z, x))(add) - === (by IH) add(z, x) + sum(xs) - === (by 5) (z + x) + sum(xs) - === (by 7) z + (x + sum(xs)) - === (by 2) z + sum(x :: xs) -``` - -Which proves `P(x :: xs)`. - -This completes the proof. diff --git a/exercises/solution-6.md b/exercises/solution-6.md deleted file mode 100644 index cc8dd02b6e7922e608ad7ab2d3eed85d7e3f3d32..0000000000000000000000000000000000000000 --- a/exercises/solution-6.md +++ /dev/null @@ -1,76 +0,0 @@ -# Exercise Session 6, Solutions - -## Question 1.1 - -```scala -// For equivalent cycles, we decide to return the cycle -// with the smallest element first. -def triangles(edges: DirectedGraph): List[(NodeId, NodeId, NodeId)] = - for { - e1 <- edges - - // The first node is the smallest of the cycle. - if (e1._1 < e1._2) - - e2 <- edges - - // The two edges are connected and - // there exists an edge between the two other end points. - if (e1._2 == e2._1 && edges.contains((e2._2, e1._1))) - - // The first node is the smallest of the cycle. - if (e1._1 < e2._2 && e2._1 != e2._2) - - } yield (e1._1, e1._2, e2._2) -``` - -An alternative solution using case extractors and references to previous values -```scala -def triangles(edges: DirectedGraph): List[(NodeId, NodeId, NodeId)] = - for { - case (a, b) <- edges; - if a < b; // The first node is the smallest of the cycle. - case (`b`, c) <- edges; - if a < c; - case (`c`, `a`) <- edges; - } yield (a, b, c) -``` - -## Question 1.2 - -```scala -def triangles(edges: DirectedGraph): List[(NodeId, NodeId, NodeId)] = - edges.filter { e1 => - e1._1 < e1._2 - }.flatMap { e1 => - edges.filter { e2 => - e1._2 == e2._1 && edges.contains((e2._2, e1._1)) - }.filter { e2 => - e1._1 < e2._2 && e2._1 != e2._2 - }.map { e2 => - (e1._1, e1._2, e2._2) - } - } -``` - -## Question 2. - -*Identity.* - -```scala - m.map(x => x) -== m.flatMap(x => unit((x => x)(x))) // Monad/Functor Consistency -== m.flatMap(x => unit(x)) // Simplification -== m.flatMap(unit) // Simplification -== m // Right unit -``` - -*Associativity.* - -```scala - m.map(h).map(g) -== m.flatMap(x => unit(h(x))).flatMap(x => unit(g(x))) // M/F Consistency -== m.flatMap(x => unit(h(x)).flatMap(x => unit(g(x)))) // Associativity -== m.flatMap(x => unit(g(h(x)))) // Left unit -== m.map(x => g(h(x))) // M/F Consistency -``` diff --git a/exercises/solution-7.md b/exercises/solution-7.md deleted file mode 100644 index 12deb5c6f49ca472108743c4fac697c279f53a30..0000000000000000000000000000000000000000 --- a/exercises/solution-7.md +++ /dev/null @@ -1,74 +0,0 @@ -# Exercise Session 7, Solutions - -## Question 1 - -1. - -```scala -1 3 1 1 2 2 2 1 -``` - -2. - -```scala -def nextLine(currentLine: List[Int]): List[Int] = { - currentLine.foldLeft(List.empty[Int]) { (acc, x) => - acc match { - case y :: count :: rest if x == y => x :: (count + 1) :: rest - case _ => x :: 1 :: acc - } - }.reverse -} -``` - -3. - -```scala -lazy val funSeq: LazyList[List[Int]] = - LazyList.cons(List(1), funSeq.map(nextLine)) -``` - -## Question 2 - -1. - -```scala -val squares: LazyList[Int] = LazyList.from(1).map(x => x * x) -``` - -2. - -```scala -lazy val codes: LazyList[String] = "0" #:: "1" #:: codes.flatMap { - (s: String) => (s + "0") #:: (s + "1") #:: LazyList.empty[String] -} -``` - -3. - -```scala -def isPalindrome(x: String): Boolean = x.reverse == x -val palCodes: LazyList[String] = codes.filter(isPalindrome) -``` - -4. - -```scala -val palCodes: LazyList[String] = for { - c <- codes - middle <- Seq("", "0", "1") -} yield c + middle + c.reverse -``` - -5. - -```scala -def interleave[A](xs: LazyList[A], ys: LazyList[A]): LazyList[A] = - (xs, ys) match { - case (x #:: xr, y #:: yr) => x #:: y #:: interleave(xr, yr) - case (LazyList.Empty, _) => ys - case (_, LazyList.Empty) => xs - } - -val allCodes = interleave(palCodes, otherCodes) -``` diff --git a/exercises/solution-8.md b/exercises/solution-8.md deleted file mode 100644 index b10018c263cf0b4080f6ea2ad343cc83079223a0..0000000000000000000000000000000000000000 --- a/exercises/solution-8.md +++ /dev/null @@ -1,88 +0,0 @@ -# Exercise Session 8, Solutions - -## Question 1 - -1. Write a `given` definition to create `Eq[List[T]]` from a `Eq[T]`. - -```scala -given EqList[T](using Eq[T]) as Eq[List[T]] { - extension (x: List[T]) - def === (y: List[T]): Boolean = - (x, y) match - case (hx :: tx, hy :: ty) => hx === hy && tx === ty - case (Nil, Nil) => true - case _ => false -} -``` - -2. Write a `given` definition to create `Eq[(T, U, S)]` from `Eq[T]`, `Eq[U]` and `Eq[S]`. - -```scala -given EqTriple[T, U, S](using Eq[T], Eq[U], Eq[S]) as Eq[(T, U, S)] { - extension (x: (T, U, S)) - def === (y: (T, U, S)): Boolean = - x._1 === y._1 && x._2 === y._2 && x._3 === y._3 -} -``` - -3. Write `given` definitions to create `Eq[Person]`. Make use of both the definitions you have written previously. - -```scala -given EqString as Eq[String] { - extension (x: String) - def === (y: String) = x == y -} - -given EqInt as Eq[Int] { - extension (x: Int) - def === (y: Int) = x == y -} - -given EqPerson(using Eq[(String, Int, List[String])]) as Eq[Person] { - extension (a: Person) - def === (b: Person) = - (a.name, a.age, a.neighbours) === (b.name, b.age, b.neighbours) -} -``` - -4. Explicitly write the `given` argument to `summon` (you may need to assign names to your `given` definitions): - -```scala -summon[Eq[Person]](using - EqPerson(using - EqTriple(using - EqString, - EqInt, - EqList(using EqString) - ) - ) -) -``` - -## Question 2 - -1. Write explicitly the `given` argument to `add` (it may help to write all type parameters explicitly): - -```scala -add(S(Z), S(S(Z)))(using - sumS(using - sumZ(using S(S(Z))) - ) -) -``` - -2. Similarly to `Sum`, write `given` definitions that create an instance of the -`Product[N, M, R]` type-class, representing the evidence that `N * M = R`. - -```scala -given [N <: Nat] as Prod[Z, N, Z] = Prod(Z) -given [N <: Nat, M <: Nat, R1 <: Nat, R2 <: Nat](using - prod: Prod[N, M, R1], - sum: Sum[R1, M, R2] -) as Prod[S[N], M, R2] = - Prod(sum.result) - -def prod[N <: Nat, M <: Nat, R <: Nat](n: N, m: M)( - using prod: Prod[N, M, R] -): R = prod.result -``` diff --git a/exercises/solution-9.md b/exercises/solution-9.md deleted file mode 100644 index df8a687d61e8785ef1fa7afd27d52d4f4613d759..0000000000000000000000000000000000000000 --- a/exercises/solution-9.md +++ /dev/null @@ -1,32 +0,0 @@ -# Exercise Session 9, Solutions - -## Question 1 - -```scala -"gcd" -> Name("Not provided (part of the lab)") -``` - -## Question 2 - -```scala -"map" -> Fun("ls", Fun("f", - Match(Name("ls"), - EmptyList, - "x", "xs", - Cons( - Call(Name("f"), Name("x")), - Call(Call(Name("map"), Name("xs")), Name("f")))))) -``` - -```scala -"foldLeft" -> Name("Not provided (part of the lab)") -``` - -## Question 3 - -```scala -"CAS" -> Fun("idx", Fun("old", Fun("new", - IfNonzero(minus(Read(Name("idx")), Name("old")), - Constant(0), - Write(Name("idx"), Name("new"), Constant(1)))))), -``` diff --git a/html.html b/html.html deleted file mode 100644 index 4b52d9fbd7f49476717adcedd84931c54edf0078..0000000000000000000000000000000000000000 --- a/html.html +++ /dev/null @@ -1,356 +0,0 @@ - - - - - - - README - - - - -

This repository will be used as the website for Functional Programming CS-210. It will be updated weekly throughout the semester. This README contains general information about the class.

- -

We will use GitLab’s issue tracker as a discussion forum. Feel free to open an issue if you have any comments or questions

-

First-week tasks

-
    -
  1. Join the Discord
  2. -
  3. Log into gitlab: https://gitlab.epfl.ch/users/sign_in
  4. -
  5. Register in a group for the exercise sessions
  6. -
  7. Follow the Tools Setup page.
  8. -
  9. Do the example lab.
  10. -
  11. Do the first graded lab.
  12. -
-

Grading

-

The grading of the course is divided between labs (25%), exercises (5%) and a final exam (70%). There will be no midterm exam this year.

-

Staff

- ---- - - - - - - - - - - - - - - - - - - - - -
RolePeople
ProfessorsMartin Odersky, Viktor Kunčak
TAsAdrien Ghosn, Dragana Milovancevic, Guillaume Martres, Nicolas Stucki, Olivier Blanvillain
Student TAsArthur Vignon, Emilien Ordonneau, Lucas Giordano, Mohamed Dhraief, Sara Djambazovska
-

Rooms

-

Lectures are prerecorded and published on this page. Exercise sessions take place on Wednesdays from 13:15 to 15:00 on Discord. Lab sessions take place on Fridays from 10:15 to 12:00, also on Discord. In the first 2 weeks of the semester, in-person office hours will be held on Wednesdays from 13:15 to 15:00 in CO 2, and on Fridays from 10:15 to 12:00 in ELA1. These office hours are there to help you setting up Git and Java our your machines and answer any other question you might have about the class. We will be happy to arrange further in-person or virtual (Discord, Zoom, etc.) office hours–just contact one of us with a list of all your available time slots.

-

Lecture Schedule

- -

Note: In some lectures, worksheets are used to present code. To learn how to use worksheets yourselves, please follow the Tools Setup and example lab. To create a new empty project to experiment with worksheets, you can clone the following repository and run code . inside as usual: git clone https://gitlab.epfl.ch/lamp/cs210-worksheets

- ------ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
WeekDateTopicVideo
116.09.2020Intro classIntroduction (slides), Elements of programming (slides), Evaluation strategies and termination (slides), Value Definitions and Conditionals (slides), Square Roots with Newtons Methods (slides), Blocks and lexical scopes (slides), Tail Recursion (slides)
223.09.2020Recursion / Function valuesHigher-Order Functions (slides), Currying (slides), Finding FixedPoints (slides), Scala Syntax Summary (slides), Functions and Data (slides), Data Abstraction (slides), Evaluation and Operators (slides)
330.09.2020ClassesClass Hierarchies (slides), How Classes are Organized (slides), Polymorphism (slides), Objects Everywhere (slides), Functions as Objects (slides)
407.10.2020ClassesDecomposition (slides), Pattern Matching (slides), Lists (slides), Enums (slides), Subtyping and Generics (slides), Variance (slides)
514.10.2020ListA closer look at lists (slides), Tuples and generic methods (slides), Higher order list functions (slides), Reduction of lists (slides), Reasoning about lists (slides)
621.10.2020CollectionOther Collections (slides), Combinatorial Search and For-Expressions (slides), Combinatorial Search Example (slides), Maps (slides), Putting the Pieces Together (slides)
728.10.2020MonadsRecap (slides), Queries with for (slides), Translation of for (slides), Functional Random Generators (slides), Monads (slides), Exceptional Monads (slides)
804.11.2020Lazy evaluationStructural Induction on Trees (slides), Lazy Lists (slides), Lazy Evaluation (slides), Infinite Sequences (slides), Case Study (slides)
911.11.2020Type-directed computationContextual abstraction (slides), Using clauses and given instances (slides), Type classes (slides), Abstract algebra and type classes (slides), Context passing (slides), Implicit function types (slides)
1018.11.2020StateFunctions and state (slides), Identity and change (slides), Loops (slides), Discrete Event Simulation (slides)
1125.11.2020Functional Reactive Programming and Constraint Propagation / Symbolic computation
1202.12.2020Interpreter
1309.12.2020Interpreter
1416.12.2020Review for the exam
-

Lab Schedule

- ------ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TitleStart DateDiscord Session (Fridays 10:15 to 12:00)Due Date (AoE)
Recursion16.09.202018.09.202027.09.2020
Functional Sets23.09.202025.09.202001.10.2020
Object-Oriented Sets30.09.202002.10.202011.10.2020 (updated after GitLab outage)
Huffman Coding07.10.202009.10.2020 & 16.10.202022.10.2020
Anagrams21.10.202023.10.202005.11.2020
Quickcheck28.10.202030.10.202012.11.2020
Bloxorz04.11.202006.11.2020 & 13.11.202019.11.2020
Codecs18.11.202020.11.2020 & 27.11.202003.12.2020
Interpreter02.12.202004.12.2020 & 11.12.202017.12.2020
-

Labs are individual assignments where you get to write Scala programs using the concepts learned during lectures. Labs are submitted by pushing your code on GitLab, see details in the grading and submission page.

-

Exercise Schedule

- ------- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TitleHandout ReleasedDiscord Session (Wednesdays 13:15 to 15:00)Due Date (AoE)Solution Released
First week tasks-16.09.2020--
Exercise Session 1-23.09.202027.09.202028.09.2020
Exercise Session 228.09.202030.09.202004.10.202005.10.2020
Exercise Session 305.10.202007.10.202011.10.202012.10.2020
Exercise Session 412.10.202014.10.202019.10.202020.10.2020
Exercise Session 520.10.202021.10.202025.10.202026.10.2020
Exercise Session 626.10.202028.10.202001.11.202002.11.2020
Exercise Session 709.11.202011.11.202015.11.202016.11.2020
Exercise Session 816.11.202018.11.202022.11.202023.11.2020
Exercise Session 923.11.202025.11.202029.11.202030.11.2020
Exercise Session 1030.11.202002.12.202006.12.202007.12.2020
Exercise Session 1107.12.202009.12.202013.12.202014.12.2020
-

Exercises are pen and paper style questions that will prepare you for the final exam. Exercises should be done in groups and submitted via Google Drive, see details in the Group workspaces page.

-

Exam Schedule

-

The final exam date will be during the exam session in January 2021. The final exam will cover all material seen during the semester.

-

Information about exam organization will be communicated by email a few days before the exam.

- - diff --git a/labs/lab-1.md b/labs/lab-1.md deleted file mode 100644 index 88cc730aa485bb770174bfb499b5f4e3e3dbc762..0000000000000000000000000000000000000000 --- a/labs/lab-1.md +++ /dev/null @@ -1,126 +0,0 @@ -# Lab 1: Recursion - -**Before starting this lab, please complete the other [first week tasks](https://gitlab.epfl.ch/lamp/cs210#first-week-tasks)!** - -# Setup - -You can use the following commands to make a fresh clone of your repository: - -```shell -git clone -b recfun git@gitlab.epfl.ch:lamp/students-repositories-fall-2020/cs210-GASPAR.git cs210-recfun -cd cs210-recfun -``` - -**If you encounter any issue with the IDE, please refer back to the -[Troubleshooting section of the example lab](example-lab.md#troubleshooting).** - -You can always refer to: - * [the example guide](https://gitlab.epfl.ch/lamp/cs210/blob/master/labs/example-lab.md) on the development workflow. - * [this guide](https://gitlab.epfl.ch/lamp/cs210/blob/master/labs/grading-and-submission.md) for details on the submission system. - **Make sure to submit your assignment before the deadline written in [README.md](/README.md)** - * [The documentation of the Scala standard library](https://www.scala-lang.org/files/archive/api/2.13.3) - * [The documentation of the Java standard - library](https://docs.oracle.com/en/java/javase/15/docs/api/index.html) - - -# Be functional! - -This course is about **functional** programming, where you'll learn to program -without using mutation, therefore you're not allowed to use the following -constructs in the labs: -- `var` -- `while` -- `return` -- Any class in the `scala.collection.mutable` package - -# Exercise 1: Pascal's Triangle - -The following pattern of numbers is called _Pascal's triangle_. - - 1 - 1 1 - 1 2 1 - 1 3 3 1 - 1 4 6 4 1 - ... - -The numbers at the edge of the triangle are all `1`, and each number -inside the triangle is the sum of the two numbers above it. Write a -function that computes the elements of Pascal's triangle by means of a -recursive process. - -Do this exercise by implementing the `pascal` function in -`Main.scala`, which takes a column `c` and a row `r`, counting from -`0` and returns the number at that spot in the triangle. For example, -`pascal(0,2)=1`, `pascal(1,2)=2` and `pascal(1,3)=3`. - -```scala -def pascal(c: Int, r: Int): Int -``` - -# Exercise 2: Parentheses Balancing - -Write a recursive function which verifies the balancing of parentheses -in a string, which we represent as a `List[Char]` not a `String`. For -example, the function should return `true` for the following strings: - -- (if (zero? x) max (/ 1 x)) -- I told him (that it's not (yet) done). - (But he wasn't listening) - -The function should return `false` for the following strings: - - -The last example shows that it's not enough to verify that a string -contains the same number of opening and closing parentheses. - -Do this exercise by implementing the `balance` function in -`Main.scala`. Its signature is as follows: - -```scala -def balance(chars: List[Char]): Boolean -``` - -There are three methods on `List[Char]` that are useful for this -exercise: - -- `chars.isEmpty: Boolean` returns whether a list is empty -- `chars.head: Char` returns the first element of the list -- `chars.tail: List[Char]` returns the list without the first element - -You can find more information on these methods in the [documentation of List](https://www.scala-lang.org/files/archive/api/2.13.3/scala/collection/immutable/List.html) - -__Hint__: you can define an inner function if you need to pass extra -parameters to your function. - -__Testing__: You can use the `toList` method to convert from a -`String` to a `List[Char]`: e.g. `"(just an) example".toList`. - -# Exercise 3: Counting Change - -Write a recursive function that counts how many different ways you can -make change for an amount, given a list of coin denominations. For -example, there are 3 ways to give change for 4 if you have coins with -denomiation 1 and 2: 1+1+1+1, 1+1+2, 2+2. - -Do this exercise by implementing the `countChange` function in -`Main.scala`. This function takes an amount to change, and a list of -unique denominations for the coins. Its signature is as follows: - -```scala -def countChange(money: Int, coins: List[Int]): Int -``` - -Once again, you can make use of functions `isEmpty`, `head` and `tail` -on the list of integers `coins`. - -__Hint__: Think of the degenerate cases. How many ways can you give -change for 0 CHF? How many ways can you give change for >0 CHF, if you -have no coins? - -# Submission - -[When you're done, don't forget to submit your assignment!](grading-and-submission.md) diff --git a/labs/lab-2.md b/labs/lab-2.md deleted file mode 100644 index 2fc17f681dfdea1cf9906fe728531faba348d0d6..0000000000000000000000000000000000000000 --- a/labs/lab-2.md +++ /dev/null @@ -1,153 +0,0 @@ -# Lab 2: Purely Functional Sets - -In this assignment, you will work with a functional representation of -sets based on the mathematical notion of characteristic functions. The -goal is to gain practice with higher-order functions. - -## Setup - -Let's upgrade the IDE support first: - -```shell -code --force --install-extension scalameta.metals -``` - -You can use the following commands to make a fresh clone of your repository: - -```shell -git clone -b funsets git@gitlab.epfl.ch:lamp/students-repositories-fall-2020/cs210-GASPAR.git cs210-funsets -cd cs210-funsets -``` - -You can always refer to: - * [the example guide](https://gitlab.epfl.ch/lamp/cs210/blob/master/labs/example-lab.md) on the development workflow. - * [this guide](https://gitlab.epfl.ch/lamp/cs210/blob/master/labs/grading-and-submission.md) for details on the submission system. - **Make sure to submit your assignment before the deadline written in [README.md](/README.md)** - * [The documentation of the Scala standard library](https://www.scala-lang.org/files/archive/api/2.13.3) - * [The documentation of the Java standard - library](https://docs.oracle.com/en/java/javase/15/docs/api/index.html) - - -## Representation - -We will work with sets of integers. - -As an example to motivate our representation, how would you represent the set of -all negative integers? You cannot list them all... one way would be to -say: if you give me an integer, I can tell you whether it's in the set -or not: for `3`, I would say `no`; for `-1`, I would say `yes`. - -Mathematically, we call the function which takes an integer as -argument and which returns a boolean indicating whether the given -integer belongs to a set, the _characteristic_ function of the -set. For example, we can characterize the set of negative integers by -the characteristic function `(x: Int) => x < 0`. - -Therefore, we choose to represent a set by its characteristic function -and define a type alias for this representation: - -```scala -type FunSet = Int => Boolean -``` - -Using this representation, we define a function that tests for the -presence of a value in a set: - -```scala -def contains(s: FunSet, elem: Int): Boolean = s(elem) -``` - -## 2.1 Basic Functions on Sets - -Let's start by implementing basic functions on sets. - -1. Define a function which creates a singleton set from one integer - value: the set represents the set of the one given element. Its - signature is as follows: - - ```scala - def singletonSet(elem: Int): FunSet - ``` - - Now that we have a way to create singleton sets, we want to define - a function that allow us to build bigger sets from smaller ones. - -2. Define the functions `union`, `intersect`, and `diff`, which takes - two sets, and return, respectively, their union, intersection and - differences. `diff(s, t)` returns a set which contains all the - elements of the set `s` that are not in the set `t`. These - functions have the following signatures: - - ```scala - def union(s: FunSet, t: FunSet): FunSet - def intersect(s: FunSet, t: FunSet): FunSet - def diff(s: FunSet, t: FunSet): FunSet - ``` - -3. Define the function `filter` which selects only the elements of a - set that are accepted by a given predicate `p`. The filtered - elements are returned as a new set. The signature of `filter` is as - follows: - - ```scala - def filter(s: FunSet, p: Int => Boolean): FunSet - ``` - -## 2.2 Queries and Transformations on Sets - -In this part, we are interested in functions used to make requests on -elements of a set. The first function tests whether a given predicate -is true for all elements of the set. This `forall` function has the -following signature: - -```scala -def forall(s: FunSet, p: Int => Boolean): Boolean -``` - -Note that there is no direct way to find which elements are in a -set. `contains` only allows to know whether a given element is -included. Thus, if we wish to do something to all elements of a set, -then we have to iterate over all integers, testing each time whether -it is included in the set, and if so, to do something with it. Here, -we consider that an integer `x` has the property `-1000 <= x <= 1000` -in order to limit the search space. - -1. Implement `forall` using linear recursion. For this, use a helper - function nested in `forall`. Its structure is as follows (replace - the `???`): - - ```scala - def forall(s: FunSet, p: Int => Boolean): Boolean = - def iter(a: Int): Boolean = - if ??? then - ??? - else if ??? then - ??? - else - iter(???) - iter(???) - ``` - -2. Using `forall`, implement a function `exists` which tests whether a - set contains at least one element for which the given predicate is - true. Note that the functions `forall` and `exists` behave like the - universal and existential quantifiers of first-order logic. - - ```scala - def exists(s: FunSet, p: Int => Boolean): Boolean - ``` - -3. Finally, write a function `map` which transforms a given set into - another one by applying to each of its elements the given - function. `map` has the following signature: - - ```scala - def map(s: FunSet, f: Int => Int): FunSet - ``` - -## Extra Hints - -- Sets are represented as functions. Think about what it _means_ for an element to belong to a set, in terms of function evaluation. For example, how do you represent a set that contains all numbers -between 1 and 100? -- Most of the solutions for this assignment can be written as one-liners. If you have more, you probably need to rethink your solution. In other words, this assignment needs more thinking (whiteboard, pen and paper) than coding ;-). -- If you are having some trouble with terminology, have a look at the [glossary](http://docs.scala-lang.org/glossary/). diff --git a/labs/lab-3.md b/labs/lab-3.md deleted file mode 100644 index 9c36488e2321f0d9e4be64d9ded46afc7eabdc48..0000000000000000000000000000000000000000 --- a/labs/lab-3.md +++ /dev/null @@ -1,134 +0,0 @@ -# Lab 3: Object-Oriented Sets - -In this assignment you will work with an object-oriented -representations based on binary trees. - -## Setup - -You can use the following commands to make a fresh clone of your repository: - -```shell -git clone -b objsets git@gitlab.epfl.ch:lamp/students-repositories-fall-2020/cs210-GASPAR.git cs210-objsets -cd cs210-objsets -``` - -You can always refer to: - * [the example guide](https://gitlab.epfl.ch/lamp/cs210/blob/master/labs/example-lab.md) on the development workflow. - * [this guide](https://gitlab.epfl.ch/lamp/cs210/blob/master/labs/grading-and-submission.md) for details on the submission system. - **Make sure to submit your assignment before the deadline written in [README.md](/README.md)** - * [The documentation of the Scala standard library](https://www.scala-lang.org/files/archive/api/2.13.3) - * [The documentation of the Java standard - library](https://docs.oracle.com/en/java/javase/15/docs/api/index.html) - - -## Overview - -For this part, you will earn credit by completing the -`TweetSet.scala` file. This file defines an abstract class `TweetSet` with -two concrete subclasses, `Empty` which represents an empty set, and -`NonEmpty(elem: Tweet, left: TweetSet, right: TweetSet)`, which represents a -non-empty set as a binary tree rooted at `elem`. The tweets are indexed by their text bodies: the bodies of all tweets on the -`left` are lexicographically smaller than `elem` and all bodies of elements on the -`right` are lexicographically greater. - -Note also that these classes are _immutable_: the -set-theoretic operations do not modify `this` but should return a new -set. - -Before tackling this assignment, we suggest you first study the -already implemented methods `contains` and `incl` for inspiration. - -## 1 Filtering - -Implement filtering on tweet sets. Complete the -stubs for the methods `filter` and `filterAcc`. `filter` takes as argument a -function, the predicate, which takes a tweet and returns a -boolean. `filter` then returns the subset of all the tweets in the -original set for which the predicate is true. For example, the -following call: - - tweets.filter(tweet => tweet.retweets > 10) - -applied to a set `tweets` of two tweets, say, where the first tweet was not retweeted -and the second tweet was retweeted 20 times should return a set containing only the -second tweet. - -Hint: start by defining the helper method `filterAcc` which takes an -accumulator set as a second argument. This accumulator contains the -ongoing result of the filtering. - - /** This method takes a predicate and returns a subset of all the elements - * in the original set for which the predicate is true. - */ - def filter(p: Tweet => Boolean): TweetSet - def filterAcc(p: Tweet => Boolean, acc: TweetSet): TweetSet - -The definition of `filter` in terms of `filterAcc` should then be -straightforward. - -## 2 Taking Unions - -Implement union on tweet sets. Complete the stub -for the method `union`. The method `union` takes another set `that`, -and computes a _new_ set which is the union of `this` and `that`, i.e. a set that contains -exactly the elements that are _either_ in `this` _or_ in `that`, _or in both_. - - def union(that: TweetSet): TweetSet - -Note that in this exercise it is your task to find out in which class(es) to -define the `union` method (should it be abstract in class `TweetSet`?). - -**Warning : This method is a crucial part of the assignment. -There are many ways to correctly code it, however some implementations run in an exponential time, so be careful, -an inefficient implementation might result in a timeout during the grading process.** - -## 3 Sorting Tweets by Their Influence - -The more often a tweet is "re-tweeted" (that is, repeated by a different user with or without -additions), the more influential it is. - -The goal of this part of the exercise is to add a method `descendingByRetweet` to `TweetSet` -which should produce a linear sequence of tweets (as an instance of class `TweetList`), ordered by -their number of retweets: - - def descendingByRetweet: TweetList - -This method reflects a common pattern when transforming data structures. While traversing one -data structure (in this case, a `TweetSet`), we're building a second data structure (here, an -instance of class `TweetList`). The idea is to start with the empty list `Nil` (containing no tweets), and to find the tweet with the most retweets in the input `TweetSet`. -This tweet is -removed from the `TweetSet` (that is, we obtain a new `TweetSet` that has all the tweets of the original set except for the tweet that was "removed"; this *immutable* set operation, `remove`, is already implemented for you), and added to the result list by creating a new `Cons`. -After that, the process repeats itself, but now we are searching through a `TweetSet` with one less -tweet. - -Hint: start by implementing the method `mostRetweeted` which returns the most popular tweet of a `TweetSet`. - -## 4 Tying everything together - -In the last step of this assignment your task is to detect influential tweets in -a set of recent tweets. We are providing you with a `TweetSet` containing several -hundred tweets from popular tech news sites in the past few days, located in the -`TweetReader` object (file "TweetReader.scala"). `TweetReader.allTweets` returns -an instance of `TweetSet` containing a set of all available tweets. - -Furthermore, you are given two lists of keywords. The first list corresponds to keywords associated with Google and Android smartphones, while the second list corresponds to keywords associated with Apple and iOS devices. Your objective is to detect which platform has generated more interest or activity in the past few days. - -As a first step, use the functionality you implemented in the first -parts of this assignment to create two different `TweetSet`s, -`googleTweets` and `appleTweets`. The first `TweetSet`, -`googleTweets`, should contain all tweets that mention (in their -"text") one of the keywords in the `google` list. The second -`TweetSet`, `appleTweets`, should contain all tweets that mention one -of the keyword in the `apple` list. Their signature is as follows: - - lazy val googleTweets: TweetSet - lazy val appleTweets: TweetSet - -Hint: use the `exists` method of `List` and `contains` method of class -`java.lang.String`. - -From the _union_ of those two `TweetSet`s, produce `trending`, an -instance of class `TweetList` representing a sequence of tweets ordered -by their number of retweets: - - lazy val trending: TweetList diff --git a/labs/lab-4.md b/labs/lab-4.md deleted file mode 100644 index 804f0eea67a8febf35faba13e89f1338dc743ed7..0000000000000000000000000000000000000000 --- a/labs/lab-4.md +++ /dev/null @@ -1,187 +0,0 @@ -# Lab 4: Pattern Matching (Huffman) - -## Setup - -You can use the following commands to make a fresh clone of your repository: - -```shell -git clone -b patmat git@gitlab.epfl.ch:lamp/students-repositories-fall-2020/cs210-GASPAR.git cs210-patmat -cd cs210-patmat -``` - -You can always refer to: - * [the example guide](https://gitlab.epfl.ch/lamp/cs210/blob/master/labs/example-lab.md) on the development workflow. - * [this guide](https://gitlab.epfl.ch/lamp/cs210/blob/master/labs/grading-and-submission.md) for details on the submission system. - **Make sure to submit your assignment before the deadline written in [README.md](/README.md)** - * [The documentation of the Scala standard library](https://www.scala-lang.org/files/archive/api/2.13.3) - * [The documentation of the Java standard - library](https://docs.oracle.com/en/java/javase/15/docs/api/index.html) - - - -## Overview - -Huffman coding is a compression algorithm that can be used to -compress lists of characters. - -In a normal, uncompressed text, each character is represented by the same number of bits (usually eight). -In Huffman coding, each character can have a bit representation of a different length, depending on how common a character is: the characters that appear often in a text are represented by a shorter bit sequence than those being used more rarely. -Every huffman code defines the specific bit sequences used to represent each character. - -A Huffman code can be represented by a binary tree whose leaves represent the characters that should be encoded. -The code tree below can represent the characters `A` to `H`. - -The leaf nodes have associated with them a weight which denotes the frequency of appearance of that character. -In the example below, the character `A` has the highest weight 8, while `F` for example has weight 1. - -Every branching node of the code tree can be thought of as a set containing the characters present in the leaves below it. The weight of a branching node is the total weight of the leaves below it: this information is necessary for the construction of the tree. - -![Example Huffman tree](http://spark-public.s3.amazonaws.com/progfun/images/huffman-table.png) - -Note that a given encoding is only optimal if the character frequencies in the encoded text match the weights in the code tree. - -Finally, observe the recursive structure of the coding tree: every sub-tree is itself a valid code tree for a smaller alphabet. - -### Encoding - -For a given Huffman tree, one can obtain the encoded -representation of a character by traversing from the root of the -tree to the leaf containing the character. Along the way, when a -left branch is chosen, a `0` is added to the representation, -and when a right branch is chosen, `1` is added to the representation. -Thus, for the Huffman tree above, the character `D` is encoded as -`1011`. - -### Decoding - -Decoding also starts at the root of the tree. Given a sequence of -bits to decode, we successively read the bits, and for each 0, we -choose the left branch, and for each 1 we choose the right branch. -When we reach a leaf, we decode the corresponding character and then -start again at the root of the tree. As an example, given the -Huffman tree above, the sequence of bits, `10001010` corresponds to -`BAC`. - -## Implementation - -In Scala, a Huffman tree can be represented as follows: - - abstract class CodeTree - case class Fork (left: CodeTree, right: CodeTree, chars: List[Char], weight: Int) extends CodeTree - case class Leaf(char: Char, weight: Int) extends CodeTree - -To begin, implement the following two (hint: very simple) functions using pattern matches on the code tree: - -1. `weight` which returns the total weight of a given Huffman tree. -`def weight(tree: CodeTree): Int = tree match ...` -2. `chars` which returns the list of characters defined in a given Huffman tree. -`def chars(tree: CodeTree): List[Char] = tree match ...` - -Using these functions, it's possible to define `makeCodeTree`, a function -which facilitates the creation of Huffman trees by automatically calculating -the list of characters and the weight when creating a node. -This function is already implemented in the handout template: - - def makeCodeTree(left: CodeTree, right: CodeTree) = - Fork(left, right, chars(left) ::: chars(right), weight(left) + weight(right)) - - -Using `makeCodeTree`, code trees can be constructed manually in the following way: - - val sampleTree = makeCodeTree( - makeCodeTree(Leaf('x', 1), Leaf('e', 1)), - Leaf('t', 2) - ) - -## Constructing Huffman Trees - -Given a text, it's possible to calculate and build an optimal Huffman -tree in the sense that the encoding of that text will be of the minimum -possible length, meanwhile keeping all information (i.e., it is lossless). - -To obtain an optimal tree from a list of characters, you have to define a function `createCodeTree` with the following signature: - - def createCodeTree(chars: List[Char]): CodeTree = ... - -Proceed with the following steps to break up this assignment into smaller parts (the handout template contains more detailed documentation): - -1. Begin by writing a function `times` which calculates the frequency -of each character in the text: -`def times(chars: List[Char]): List[(Char, Int)] = ...` -2. Then, write a function `makeLeafList` which generates a list containing -all the leaves of the Huffman tree to be constructed (the case `Leaf` of -the algebraic datatype `CodeTree`). -The list should be ordered by ascending weights where the -weight of a leaf is the number of times (or the frequency) it appears in -the given text. -`def makeOrderedLeafList(freqs: List[(Char, Int)]): List[Leaf] = ...` -3. Write a simple function `singleton` which checks whether a list of code trees contains only one single tree. -`def singleton(trees: List[CodeTree]): Boolean = ...` -4. Write a function `combine` which (1) removes the two trees with the -lowest weight from the list constructed in the previous step, and (2) -merges them by creating a new node of type `Fork`. Add this new tree -to the list - which is now one element shorter - while preserving the -order (by weight). -`def combine(trees: List[CodeTree]): List[CodeTree] = ...` -5. Write a function `until` which calls the two functions defined above -until this list contains only a single tree. This tree is the optimal -coding tree. The function `until` can be used in the following way: -`until(singleton, combine)(trees)` -where the argument `trees` is of the type `List[CodeTree]`. -6. Finally, use the functions defined above to implement the function -`createCodeTree` which respects the signature shown above. - -## Decoding - -Define the function `decode` which decodes a list of bits (which were -already encoded using a Huffman tree), given the corresponding coding -tree. - - type Bit = Int - def decode(tree: CodeTree, bits: List[Bit]): List[Char] = ... - -Use this function and the `frenchCode` code tree to decode the bit sequence in `secret`. -Store the resulting character sequence in `decodedSecret`. - -## Encoding - -This section deals with the Huffman encoding of a sequence of characters -into a sequence of bits. - -### ...Using a Huffman Tree - -Define the function `encode` which encodes a list of characters using -Huffman coding, given a code tree. - - def encode(tree: CodeTree)(text: List[Char]): List[Bit] = ... - -Your implementation must traverse the coding tree for each character, -a task that should be done using a helper function. - -### ...Using a Coding Table - -The previous function is simple, but very inefficient. You goal is now -to define `quickEncode` which encodes an equivalent representation, but -more efficiently. - - def quickEncode(tree: CodeTree)(text: List[Char]): List[Bit] = ... - -Your implementation will build a coding table once which, for each possible -character, gives the list of bits of its code. The simplest way - but not -the most efficient - is to encode the table of characters as a list of pairs. - - type CodeTable = List[(Char, List[Bit])] - -The encoding must then be done by accessing the table, via a function -`codeBits`. - - def codeBits(table: CodeTable)(char: Char): List[Bit] = ... - -The creation of the table is defined by `convert` which traverses the coding -tree and constructs the character table. - - def convert(t: CodeTree): CodeTable = ... - -Implement the function `convert` by using the function `mergeCodeTables` below: - - def mergeCodeTables(a: CodeTable, b: CodeTable): CodeTable = ... diff --git a/labs/lab-5.md b/labs/lab-5.md deleted file mode 100644 index 1842e6f787301d166a18db7a25ea6f477fc4a50d..0000000000000000000000000000000000000000 --- a/labs/lab-5.md +++ /dev/null @@ -1,284 +0,0 @@ -# Lab 5: For-comprehensions and Collections - -In this assignment, you will solve the combinatorial problem of finding all -the anagrams of a sentence using the Scala Collections API and for-comprehensions. - -You are encouraged to look at the Scala API documentation while solving this -exercise, which can be found here: - -[http://www.scala-lang.org/api/current/index.html](http://www.scala-lang.org/api/current/index.html) - -Note that Scala uses the `String` from Java, therefore the documentation -for strings has to be looked up in the Javadoc API: - -[http://docs.oracle.com/javase/6/docs/api/java/lang/String.html](http://docs.oracle.com/javase/6/docs/api/java/lang/String.html) - -## Setup - -You can use the following commands to make a fresh clone of your repository: - -```shell -git clone -b forcomp git@gitlab.epfl.ch:lamp/students-repositories-fall-2020/cs210-GASPAR.git cs210-forcomp -cd cs210-forcomp -``` - -You can always refer to: - * [the example guide](https://gitlab.epfl.ch/lamp/cs210/blob/master/labs/example-lab.md) on the development workflow. - * [this guide](https://gitlab.epfl.ch/lamp/cs210/blob/master/labs/grading-and-submission.md) for details on the submission system. - **Make sure to submit your assignment before the deadline written in [README.md](/README.md)** - * [The documentation of the Scala standard library](https://www.scala-lang.org/files/archive/api/2.13.3) - * [The documentation of the Java standard - library](https://docs.oracle.com/en/java/javase/15/docs/api/index.html) - - -## The problem - -An anagram of a word is a rearrangement of its letters such that a word with -a different meaning is formed. For example, if we rearrange the letters of -the word `Elvis` we can obtain the word `lives`, which is one of its anagrams. - -In a similar way, an anagram of a sentence is a rearrangement of all the -characters in the sentence such that a new sentence is formed. The new -sentence consists of meaningful words, the number of which may or may not -correspond to the number of words in the original sentence. For example, -the sentence: - - I love you - -is an anagram of the sentence: - - You olive - -In this exercise, we will consider permutations of words anagrams of -the sentence. In the above example: - - You I love - -is considered a separate anagram. - -When producing anagrams, we will ignore character casing and -punctuation characters. - -Your ultimate goal is to implement a method `sentenceAnagrams`, which, -given a list of words representing a sentence, finds all the anagrams -of that sentence. Note that we used the term _meaningful_ in defining -what anagrams are. You will be given a dictionary, i.e. a list of words -indicating words that have a meaning. - -Here is the general idea. We will transform the characters of the sentence -into a list saying how often each character appears. We will call this -list _the occurrence list_. To find anagrams of a word we will find all -the words from the dictionary which have the same occurrence list. -Finding an anagram of a sentence is slightly more difficult. We will -transform the sentence into its occurrence list, then try to extract any -subset of characters from it to see if we can form any meaningful words. -From the remaining characters we will solve the problem recursively and -then combine all the meaningful words we have found with the recursive -solution. - -Let's apply this idea to our example, the sentence `You olive`. Lets -represent this sentence as an occurrence list of characters `eiloouvy`. We start -by subtracting some subset of the characters, say `i`. We are left with -the characters `eloouvy`. - -Looking into the dictionary we see that `i` corresponds to word `I` in -the English language, so we found one meaningful word. We now solve the -problem recursively for the rest of the characters `eloouvy` and obtain -a list of solutions `List(List(love, you), List(you, love))`. We can combine -`I` with that list to obtain sentences `I love you` and `I you love`, -which are both valid anagrams. - -## Representation - -We represent the words of a sentence with the `String` data type: - - type Word = String - -Words contain lowercase and uppercase characters, and no whitespace, -punctuation or other special characters. - -Since we are ignoring the punctuation characters of the sentence -as well as the whitespace characters, we will represent sentences -as lists of words: - - type Sentence = List[Word] - -We mentioned previously that we will transform words and sentences into -occurrence lists. We represent the occurrence lists as sorted lists of -character and integers pairs: - - type Occurrences = List[(Char, Int)] - -The list should be sorted by the characters in an ascending order. -Since we ignore the character casing, all the characters in the occurrence -list have to be lowercase. -The integer in each pair denotes how often the character appears in a -particular word or a sentence. This integer must be positive. Note that -positive also means non-zero -- characters that do not appear in the -sentence do not appear in the occurrence list either. - -Finally, the dictionary of all the meaningful English words is represented -as a `List` of words: - - val dictionary: List[Word] = loadDictionary - -The dictionary already exists for this exercise and is loaded for you using -the `loadDictionary` utility method. - -## Computing Occurrence Lists - -The `groupBy` method takes a function mapping an element of a collection to a -key of some other type, and produces a `Map` of keys and collections of -elements which mapped to the same key. This method _groups_ the elements, -hence its name. - -Here is one example: - - List("Every", "student", "likes", "Scala").groupBy((element: String) => element.length) - -produces: - - Map( - 5 -> List("Every", "likes", "Scala"), - 7 -> List("student") - ) - -Above, the key is the `length` of the string and the type of the key is `Int`. Every -`String` with the same `length` is grouped under the same key -- its `length`. - -Here is another example: - - List(0, 1, 2, 1, 0).groupBy((element: Int) => element) - -produces: - - Map( - 0 -> List(0, 0), - 1 -> List(1, 1), - 2 -> List(2) - ) - -`Map`s provide efficient lookup of all the values mapped to a certain key. Any collection -of pairs can be transformed into a `Map` using the `toMap` method. Similarly, any `Map` can -be transformed into a `List` of pairs using the `toList` method. - -In our case, the collection will be a `Word` (i.e. a `String`) and its elements are -characters, so the `groupBy` method takes a function mapping characters into a desired -key type. - -In the first part of this exercise, we will implement the method `wordOccurrences` -which, given a word, produces its occurrence list. In one of the previous exercises, -we produced the occurrence list by recursively traversing a list of characters. -This time we will use the `groupBy` method from the Collections API (hint: you -may additionally use other methods, such as `map` and `toList`). - - def wordOccurrences(w: Word): Occurrences - -Next, we implement another version of the method for entire sentences. -We can concatenate the words of the sentence into a single word and then reuse -the method `wordOccurrences` that we already have. - - def sentenceOccurrences(s: Sentence): Occurrences - -## Computing Anagrams of a Word - -To compute the anagrams of a word, we use the simple observation that all the anagrams -of a word have the same occurrence list. To allow efficient lookup of all the words -with the same occurrence list, we will have to _group_ the words of the dictionary -according to their occurrence lists. - - lazy val dictionaryByOccurrences: Map[Occurrences, List[Word]] - -We then implement the method `wordAnagrams` which returns the list of anagrams of -a single word: - - def wordAnagrams(word: Word): List[Word] - -## Computing Subsets of a Set - -To compute all the anagrams of a sentence, we will need a helper method which, -given an occurrence list, produces all the subsets of that occurrence list. - - def combinations(occurrences: Occurrences): List[Occurrences] - -The `combinations` method should return all possible ways in which we can pick -a subset of characters from `occurrences`. For example, given the occurrence list: - - List(('a', 2), ('b', 2)) - -the list of all subsets is: - - List( - List(), - List(('a', 1)), - List(('a', 2)), - List(('b', 1)), - List(('a', 1), ('b', 1)), - List(('a', 2), ('b', 1)), - List(('b', 2)), - List(('a', 1), ('b', 2)), - List(('a', 2), ('b', 2)) - ) - -The order in which you return the subsets does not matter as long as they are -all included. Note that there is only one subset of an empty occurrence list, -and that is the empty occurrence list itself. - -Hint: investigate how you can use for-comprehensions to implement parts of this method. - -## Computing Anagrams of a Sentence - -We now implement another helper method called `subtract` which, given two occurrence -lists `x` and `y`, subtracts the frequencies of the occurrence list `y` from the -frequencies of the occurrence list `x`: - - def subtract(x: Occurrences, y: Occurrences): Occurrences - -For example, given two occurrence lists for words `lard` and `r`: - - val x = List(('a', 1), ('d', 1), ('l', 1), ('r', 1)) - val y = List(('r', 1)) - -the `subtract(x, y)` is `List(('a', 1), ('d', 1), ('l', 1))`. - -The precondition for the `subtract` method is that the occurrence list `y` is -a subset of the occurrence list `x` -- if the list `y` has some character then -the frequency of that character in `x` must be greater or equal than the -frequency of that character in `y`. -When implementing `subtract` you can assume that `y` is a subset of `x`. - -Hint: you can use `foldLeft`, and `-`, `apply` and `updated` operations on `Map`. - -Now we can finally implement our `sentenceAnagrams` method for sequences. - - def sentenceAnagrams(sentence: Sentence): List[Sentence] - -Note that the anagram of the empty sentence is the empty sentence itself. - -Hint: First of all, think about the recursive structure of the problem: what -is the base case, and how should the result of a recursive invocation be integrated -in each iteration? Also, using for-comprehensions helps in finding an elegant -implementation for this method. - -Test the `sentenceAnagrams` method on short sentences, no more than 10 characters. -The combinations space gets huge very quickly as your sentence gets longer, -so the program may run for a very long time. However for sentences such as -`Linux rulez`, `I love you` or `Mickey Mouse` the program should end fairly -quickly -- there are not many other ways to say these things. - - -## Further Improvement (Optional) - -This part is optional and is not part of an assignment, nor will be graded. -You may skip this part freely. - -The solution with enlisting all the combinations was concise, but it was not very efficient. -The problem is that we have recomputed some anagrams more than once when recursively -solving the problem. -Think about a concrete example and a situation where you compute the anagrams of the same -subset of an occurrence list multiple times. - -One way to improve the performance is to save the results obtained the first time -when you compute the anagrams for an occurence list, and use the stored result if -you need the same result a second time. -Try to write a new method `sentenceAnagramsMemo` which does this. diff --git a/labs/lab-6.md b/labs/lab-6.md deleted file mode 100644 index 2d4caf9796abfa3f4453cbbb55866ecb39bdaeb4..0000000000000000000000000000000000000000 --- a/labs/lab-6.md +++ /dev/null @@ -1,159 +0,0 @@ -# Lab 6: QuickCheck - -You can use the following commands to make a fresh clone of your repository: - -```shell -git clone -b quickcheck git@gitlab.epfl.ch:lamp/students-repositories-fall-2020/cs210-GASPAR.git cs210-quickcheck -cd cs210-quickcheck -``` - -You can always refer to: - * [the example guide](https://gitlab.epfl.ch/lamp/cs210/blob/master/labs/example-lab.md) on the development workflow. - * [this guide](https://gitlab.epfl.ch/lamp/cs210/blob/master/labs/grading-and-submission.md) for details on the submission system. - **Make sure to submit your assignment before the deadline written in [README.md](/README.md)** - * [The documentation of the Scala standard library](https://www.scala-lang.org/files/archive/api/2.13.3) - * [The documentation of the Java standard - library](https://docs.oracle.com/en/java/javase/15/docs/api/index.html) - - -## QuickCheck - -In this assignment, you will work with the -[ScalaCheck](https://github.com/rickynils/scalacheck/blob/master/doc/UserGuide.md) -library for automated specification-based testing. - -You're given several implementations of a purely functional data -structure: a heap, which is a priority queue supporting operations `insert`, `meld`, -`findMin`, `deleteMin`. Here is the interface: - - trait Heap { - type H // type of a heap - type A // type of an element - def ord: Ordering[A] // ordering on elements - - def empty: H // the empty heap - def isEmpty(h: H): Boolean // whether the given heap h is empty - - def insert(x: A, h: H): H // the heap resulting from inserting x into h - def meld(h1: H, h2: H): H // the heap resulting from merging h1 and h2 - - def findMin(h: H): A // a minimum of the heap h - def deleteMin(h: H): H // a heap resulting from deleting a minimum of h - } - -All these operations are _pure_; they never modify the given heaps, -and may return new heaps. This purely functional interface is taken -from Brodal & Okasaki's paper, [_Optimal Purely Functional Priority Queues_](http://www.brics.dk/RS/96/37/BRICS-RS-96-37.pdf). - -A priority queue is a queue, in which each element is assigned a "priority". In -classical queues, elements can be retrieved in first-in, first-out order, whereas -in a priority queue, elements are retrieved as per the priority they are assigned. -As such, classical queues are therefore just priority queues where the priority is -the order in which elements are inserted. - -As seen in the above interface, we can create a queue by - - * instantiating an `empty` queue. - * `insert`ing an element into a queue (with an attached priority), thereby creating - a new queue. - * `meld`ing two queues, which results in a new queue that contains all the elements of the - first queue and all the elements of the second queue. - -In addition, we can can test whether a queue is empty or not with `isEmpty`. If -you have a non-empty queue, you can find its minimum with `findMin`. You can also -get a smaller queue from a non-empty queue by deleting the minimum element with -`deleteMin`. In this assignment, the heap operates on `Int` elements with their -values as priorities, so `findMin` finds the least integer in the heap. - -You are given multiple implementations of `IntHeaps` in file -`src/main/scala/quickcheck/Heap.scala`. Only one of these is correct, while -the other ones have bugs. Your goal is to write some properties that will -be automatically checked. All the properties you write should be -satisfiable by the correct implementation, while at least one of them -should fail in each incorrect implementation, thus revealing it's buggy. - -You should write your properties in the body of the `QuickCheckHeap` -class in the file `src/main/scala/quickcheck/QuickCheck.scala`. - -## Part 1: A Heap Generator - -Before checking properties, we must first generate some heaps. Your first task is -to implement such a generator: - - lazy val genHeap: Gen[H] = ??? - -For doing this, you can take inspiration from the lecture on generators and monads. -Here are some basic generators that you can combine together to create larger ones: - - * `arbitrary[T]` is a generator that generates an arbitrary value of type `T`. As we are interested in `IntHeaps` it will generate arbitrary integer values, uniformly at random. - * `oneOf(gen1, gen2)` is a generator that picks one of `gen1` or `gen2`, uniformly - at random. - * `const(v)` is a generator that will always return the value `v`. - -You can find many more useful ones either in the ScalaCheck [user guide](https://github.com/rickynils/scalacheck/blob/master/doc/UserGuide.md) or in -the [Scaladocs](https://javadoc.io/doc/org.scalacheck/scalacheck_2.13/1.14.2/org/scalacheck/Gen$.html). - -For instance, we can write a generator for maps of type `Map[Int, Int]` as follows: - - lazy val genMap: Gen[Map[Int,Int]] = oneOf( - const(Map.empty[Int,Int]), - for - k <- arbitrary[Int] - v <- arbitrary[Int] - m <- oneOf(const(Map.empty[Int,Int]), genMap) - yield - m.updated(k, v) - ) - -## Part 2: Writing properties - -Now that you have a generator, you can write property-based tests. The idea behind -property-based testing is to verify that certain properties hold on your -implementations. Instead of specifying exactly which inputs our properties should -satisfy, we instead generate random inputs, and run each property test on these -randomly generated inputs. This way we increase the likelihood that our implementation -is correct. - -For example, we would like to check that adding a single element to an empty heap, -and then removing this element, should yield the element in question. We would -write this requirement as follows: - - property("min1") = forAll { (a: Int) => - val h = insert(a, empty) - findMin(h) == a - } - -Another property we might be interested in is that, for any heap, adding the minimal -element, and then finding it, should return the element in question: - - property("gen1") = forAll { (h: H) => - val m = if isEmpty(h) then 0 else findMin(h) - findMin(insert(m, h)) == m - } - -In `src/main/scala/quickcheck/QuickCheck.scala`, write some more properties that -should be satisfied. Your properties should at least cover the following relevant facts: - - * If you insert any two elements into an empty heap, finding the - minimum of the resulting heap should get the smallest of the two - elements back. - - * If you insert an element into an empty heap, then delete the - minimum, the resulting heap should be empty. - - * Given any heap, you should get a sorted sequence of elements when - continually finding and deleting minima. (Hint: recursion and helper - functions are your friends.) - - * Finding a minimum of the melding of any two heaps should return a - minimum of one or the other. - - -In order to get full credit, all tests should pass, that is you should -correctly identify each buggy implementation while only writing -properties that are true of heaps. Your properties should cover all of the above-stated relevant facts. -You are free to write as many or as few properties as you want in order to achieve a full passing suite. - -Note that this assignment asks you to write tests whose content captures all of the above relevant facts, -and whose execution correctly differentiates correct from incorrect heaps among the heaps given to you. -You need not worry about additional buggy heaps that someone else might write. diff --git a/labs/lab-7.md b/labs/lab-7.md deleted file mode 100644 index 696cd64d21639f9cbe4edbd60b16b2f7f68353fb..0000000000000000000000000000000000000000 --- a/labs/lab-7.md +++ /dev/null @@ -1,208 +0,0 @@ -# Lab 7: Bloxorz (Streams) - -You can use the following commands to make a fresh clone of your repository: - -```shell -git clone -b streams git@gitlab.epfl.ch:lamp/students-repositories-fall-2020/cs210-GASPAR.git cs210-streams -cd cs210-streams -``` - -You can always refer to: - * [the example guide](https://gitlab.epfl.ch/lamp/cs210/blob/master/labs/example-lab.md) on the development workflow. - * [this guide](https://gitlab.epfl.ch/lamp/cs210/blob/master/labs/grading-and-submission.md) for details on the submission system. - **Make sure to submit your assignment before the deadline written in [README.md](/README.md)** - * [The documentation of the Scala standard library](https://www.scala-lang.org/files/archive/api/2.13.3) - * [The documentation of the Java standard - library](https://docs.oracle.com/en/java/javase/15/docs/api/index.html) - - -## QuickCheck - -In this assignment you will implement a solver for a simplified version of a Flash game -named "Bloxorz" using lazy evaluation. - -As in the previous assignments, you are encouraged to look at the Scala API documentation while solving this exercise, which can be found here: - -[http://www.scala-lang.org/api/current/index.html](http://www.scala-lang.org/api/current/index.html) - -## Bloxorz - -Bloxorz is a game in Flash, which you can access [here](https://www.coolmathgames.com/0-bloxorz). As a first step for this assignment, *play it* for a few levels. - -The objective of Bloxorz is simple; you must navigate your rectangular block to the hole at the end of the board, by rolling it, in the fewest number of moves possible. A block can be moved in 4 possible directions, left, right, up, down, using the appropriate keys on the keyboard. - -You will quickly notice that for many levels, you are, in your head, trying to walk through different configurations/positions of where the block can be in order to reach it to the goal position. Equipped with some new programming skills, you can now let your computer do the work! - -The idea of this assignment is to code a solver for a simplified version of this game, with no orange tiles, circles or crosses on the terrain. The goal of your program, given a terrain configuration with a start position and a goal position, is to return the _exact_ sequence of keys to type in order to reach the goal position. Naturally, we will be interested in getting the _shortest_ path as well. - -### State-space Exploration - -The theory behind coding a solver for this game is in fact be applicable to many different problems. The general problem we are trying to solve is the following: - * We start at some initial state `S`, and we are trying to reach an end state `T`. - * From every state, there are possible transitions to other states, some of which are out of bounds. - * We explore the states, starting from `S`. by exploring its neighbors and following the chain, until we reach `T`. There are different ways of exploring the state space. On the two ends of the spectrum are the following techniques: - * **depth-first search**: when we see a new state, we immediately explore its direct neighbors, and we do this all the way down, until we reach a roadblock. Then we backtrack until the first non-explored neighbor, and continue in the same vein. - * **breadth-first search**: here, we proceed more cautiously. When we find the neighbors of our current state, we explore each of them for each step. The respective neighbors of these states are then stored to be explored at a later time. - -## Game Setup - -Let us start by setting up our platform. The trait `GameDef` will contain all the logic regarding how the terrain is setup, the blocks are represented and how they move. - -### Positions - -A position on the game board is represented using the `case class Pos(x:Int, y:Int)`, where `x` and `y` represent its coordinates. The scaladoc comment on class `Pos` explains how to interpret the coordinates: - -- The `x` coordinate denotes the position on the vertical axis -- The `y` coordinate is used for the horizontal axis -- The coordinates increase when moving down and right - -Illustration: - - 0 1 2 3 <- y axis - 0 o o o o - 1 o o o o - 2 o # o o # is at position Pos(2, 1) - 3 o o o o - - ^ - | - - x axis - - -### The Terrain - -We represent our terrain as a function from positions to booleans: - - type Terrain = Pos => Boolean - -The function returns `true` for every position that is inside the terrain. Terrains can be created easily from a string representation using the methods in the file `StringParserTerrain.scala`. - -Your first task is to implement two methods in trait `StringParserTerrain` that are used to parse the terrain and the start / end positions. The Scaladoc comments give precie instructions how they should be implemented. - - def terrainFunction(levelVector: Vector[Vector[Char]]): Pos => Boolean = ??? - def findChar(c: Char, levelVector: Vector[Vector[Char]]): Pos = ??? - - - - -### Blocks - -Back in the file `GameDef.scala`, a block is a 2 x 1 x 1 cuboid. We represent it as a case class which contains two fields, the 2d position of both the cubes which make up the block. - -A `Block` is therefore a `case class Block(b1: Pos, b2: Pos)`, and can move in four different directions, each time yielding a new block. To this effect, the methods `left`, `right`, `up` and `down` are provided. - -Given this, you can now define a method `isStanding` which tells us whether the Block is standing or not: - - def isStanding: Boolean = ??? - - -Next, implement a method `isLegal` on Block which tells us whether a block is on the terrain or off it: - - def isLegal: Boolean = ??? - -Finally, we need to implement a method that constructs the initial block for our simulation, the block located at the start position: - - def startBlock: Block = ??? - - -### Moves and Neighbors - -To record which moves we make when navigating the block, we represent the four possible moves as case objects: - - sealed abstract class Move - case object Left extends Move - case object Right extends Move - case object Up extends Move - case object Down extends Move - -You can now implement the functions `neighbors` and `legalNeighbors` on `Block`, which return a list of tuples: the neighboring blocks, as well as the move to get there. - - def neighbors: List[(Block,Move)] = ??? - def legalNeighbors: List[(Block,Move)] = ??? - - -## Solving the Game - -Now that everything is set up, we can concentrate on actually coding our solver which is defined in the file `Solver.scala`. - -We could represent a path to a solution as a `LazyList[Block]`. We however also need to make sure we keep the history on our way to the solution. Therefore, a path is represented as a `LazyList[(Block, List[Move])]`, where the second part of the pair records the history of moves so far. Unless otherwise noted, the last move is the `head` element of the `List[Move]`. - -First, implement a function `done` which determines when we have reached the goal: - - def done(b: Block): Boolean = ??? - -#### Finding Neighbors - -Then, implement a function `neighborsWithHistory`, which, given a block, and its history, returns a lazy list of neighboring blocks with the corresponding moves. - - def neighborsWithHistory(b: Block, history: List[Move]): LazyList[(Block, List[Move])] = ??? - -As mentioned above, the history is ordered so that the most recent move is the head of the list. If you consider Level 1 as defined in `Bloxorz.scala`, then - - neighborsWithHistory(Block(Pos(1,1),Pos(1,1)), List(Left,Up)) - -results in a lazy list with the following elements (given as a set): - - Set( - (Block(Pos(1,2),Pos(1,3)), List(Right,Left,Up)), - (Block(Pos(2,1),Pos(3,1)), List(Down,Left,Up)) - ) - -You should implement the above example as a test case in the test suite `BloxorzSuite`. - -Hint: You can convert a `List`, a `Set` or any other collection into a -`LazyList` by calling `.to(LazyList)` on it. - -#### Avoiding Circles - -While exploring a path, we will also track all the blocks we have seen so far, so as to not get lost in circles of movements (such as sequences of left-right-left-right). Implement a function `newNeighborsOnly` to this effect: - - def newNeighborsOnly(neighbors: LazyList[(Block, List[Move])], - explored: Set[Block]): LazyList[(Block, List[Move])] = ??? - -Example usage: - - newNeighborsOnly( - Set( - (Block(Pos(1,2),Pos(1,3)), List(Right,Left,Up)), - (Block(Pos(2,1),Pos(3,1)), List(Down,Left,Up)) - ).to(LazyList), - - Set(Block(Pos(1,2),Pos(1,3)), Block(Pos(1,1),Pos(1,1))) - ) - -returns - - Set( - (Block(Pos(2,1),Pos(3,1)), List(Down,Left,Up)) - ).to(LazyList) - -Again, you should convert this example into a test case. - -#### Finding Solutions - -Now to the crux of the solver. Implement a function `from`, which, given an initial lazy list and a set of explored blocks, creates a lazy list containing the possible paths starting from the head of the initial lazy list: - - def from(initial: LazyList[(Block, List[Move])], - explored: Set[Block]): LazyList[(Block, List[Move])] = ??? - -Note: pay attention to how the path is constructed: as discussed in the introduction, the key to getting the shortest path for the problem is to explore the space in a breadth-first manner. - -Hint: The case study lecture about the water pouring problem (7.5) might help you. - -#### Putting Things together - -Finally we can define a `lazy val pathsFromStart` which is a lazy list of all the paths that begin at the starting block: - - lazy val pathsFromStart: LazyList[(Block, List[Move])] = ??? - -We can also define `pathToGoal` which is a lazy list of all possible pairs of goal blocks along with their history. Indeed, there can be more than one road to Rome! - - lazy val pathsToGoal: LazyList[(Block, List[Move])] = ??? - -To finish it off, we define `solution` to contain the (or one of the) shortest list(s) of moves that lead(s) to the goal. - -**Note: the `head` element of the returned `List[Move]` should represent the first move that the player should perform from the starting position.** - - lazy val solution: List[Move] = ??? diff --git a/labs/lab-8.md b/labs/lab-8.md deleted file mode 100644 index 0bd4b981c895976bce9fbc22da16e349bd3b291e..0000000000000000000000000000000000000000 --- a/labs/lab-8.md +++ /dev/null @@ -1,283 +0,0 @@ -# Lab 8: Type-Directed Programming (Codecs) - -You can use the following commands to make a fresh clone of your repository: - -```shell -git clone -b codecs git@gitlab.epfl.ch:lamp/students-repositories-fall-2020/cs210-GASPAR.git cs210-codecs -cd cs210-codecs -``` - -You can always refer to: - * [the example guide](https://gitlab.epfl.ch/lamp/cs210/blob/master/labs/example-lab.md) on the development workflow. - * [this guide](https://gitlab.epfl.ch/lamp/cs210/blob/master/labs/grading-and-submission.md) for details on the submission system. - **Make sure to submit your assignment before the deadline written in [README.md](/README.md)** - * [The documentation of the Scala standard library](https://www.scala-lang.org/files/archive/api/2.13.3) - * [The documentation of the Java standard - library](https://docs.oracle.com/en/java/javase/15/docs/api/index.html) - - -## Overview - -The goal of this assignment is to implement a small serialization library. This library -will be able to encode Scala values (such as instances of case classes) into [JSON] documents -that can be sent over the wire (or saved to a file). Conversely, the library will be able -to decode JSON documents as Scala values. JSON serialization is often used by web servers. - -Please make sure you are familiar with the [JSON] serialization format before starting -the assignment. - -The library will follow a "type-directed" approach. This means that it will use a type-class -`Encoder[A]` to model the ability of encoding a value of type `A` into JSON, and a type-class -`Decoder[A]` to model the ability of decoding a JSON document as a value of type `A`. - -First, you will define given instances for simple types (e.g., `Int`, `String`, etc.). -Then, you will define conditional instances to combine encoders and decoders together to -handle more complex types. - -To make things easier, -the encoders and decoders implemented in this assignment don’t directly work with -JSON blobs, but they work with an intermediate `Json` data type: - -~~~ -enum Json: - /** The JSON `null` value */ - case Null - /** JSON boolean values */ - case Bool(value: Boolean) - /** JSON numeric values */ - case Num(value: BigDecimal) - /** JSON string values */ - case Str(value: String) - /** JSON objects */ - case Obj(fields: Map[String, Json]) - /** JSON arrays */ - case Arr(items: List[Json]) -~~~ - -Here is an example of a JSON value: - -~~~ -{ - "foo": 0, - "bar": [true, false] -} -~~~ - -It can be represented as follows with the `Json` data type: - -~~~ -Json.Obj(Map( - "foo" -> Json.Num(0), - "bar" -> Json.Arr(Json.Bool(true), Json.Bool(false)) -)) -~~~ - -With this `Json` type being defined, **encoding** a value of type `A` consists -of transforming it into a value of type `Json`: - -~~~ -trait Encoder[-A]: - /** Encodes a value of type `A` into JSON */ - def encode(value: A): Json -~~~ - -Unlike arbitrary JSON values, JSON objects have the special property that -two objects can be combined to build a bigger JSON object containing both -objects’ fields. We use this combining property of JSON objects to define -the `zip` method on `Encoder` returning JSON objects. We do so in a -subclass of `Encoder` called `ObjectEncoder`: - -~~~ -trait ObjectEncoder[-A] extends Encoder[A]: - // Refines the encoding result to `Json.Obj` - def encode(value: A): Json.Obj - - def zip[B](that: ObjectEncoder[B]): ObjectEncoder[(A, B)] = ... -~~~ - -Conversely, **decoding** a value of type `A` consists in transforming -a value of type `Json` into a value of type `A`: - -~~~ -trait Decoder[+A]: - /** - * @param data The data to de-serialize - * @return The decoded value wrapped in `Some`, or `None` if decoding failed - */ - def decode(data: Json): Option[A] -~~~ - -Note that the decoding operation returns an `Option[A]` instead of just -an `A` because the decoding process can fail (`None` means that it was -impossible to produce an `A` from the supplied JSON data). - -Given instances of encoders and decoders are defined in the `EncoderInstances` -and `DecoderInstances` traits, respectively. These traits are inherited by the `Encoder` -and `Decoder` companion objects, respectively. By doing so, we make the given -instances automatically available, removing the need for explicitly importing them. - -We provide you with a `parseJson` function that parses a JSON blob from a `String` into -a `Json` value, and a `renderJson` function that turns a `Json` value into a JSON -blob in a `String`. You can use them to experiment with the encoders and decoders that -you write. The functions `parseJson` and `renderJson` are defined in the file -`Util.scala`. - -## Your Task - -Your work consists in writing codec instances that can be combined together to build -codec instances for more complex types. You will start by writing instances for basic -types (such as `String` or `Boolean`) and you will end up writing instances for -case classes. - -Open the file `Codecs.scala`. It contains the definition of the types `Json`, -`Encoder`, `ObjectEncoder` and `Decoder`. Complete the partially implemented given instance -definitions (replace the `???` with proper implementations) and introduce new given instance -definitions as needed (look for `TODO` comments). - -At any time, you can follow your progress by running the `test` sbt task. You can -also use the `run` sbt task to run the `Main` program defined at the bottom of -the `codecs.json` file. Last, you can create a REPL session with the `console` sbt -task, and then experiment with your code: - -~~~ -sbt:progfun2-codecs> console - -scala> import codecs._ -import codecs._ - -scala> Util.parseJson(""" { "name": "Bob", "age": 10 } """) -val res0: Option[codecs.Json] = Some(Obj(Map(name -> Str(Bob), age -> Num(10)))) - -scala> res0.flatMap(_.decodeAs[Person]) // This will crash until you implement it in this assignment -val res1: Option[codecs.Person] = Some(Person(Bob,10)) - -scala> summon[Encoder[Int]] -val res2: codecs.Encoder[Int] = codecs.Encoder$$anon$1@74d8fde0 - -scala> res2.encode(42) -val res3: codecs.Json = Num(42) - -scala> :quit -~~~ - -Remember that if you make any change to the code, you'll need to quit the console -and start it again to run the updated code. Alternatively and as usual you can -also use a worksheet. - -### Basic Codecs - -Start by implementing the given instances of `Encoder[String]` and `Encoder[Boolean]`, -and the corresponding given instances of `Decoder[Int]`, `Decoder[String]` and `Decoder[Boolean]`. - -Make sure that your `Int` decoder rejects JSON floating point numbers. - -#### Troubleshooting - -#### Error when compiling -If `compile` fails with a long error message containing: -```scala -[error] scala.MatchError: ClassInfo(... -``` -It means you're running on Java >= 14. We only support Java 8 in this -course, refer back to [Step 2 of the Tools -Setup page](https://gitlab.epfl.ch/lamp/cs210/-/blob/master/labs/tools-setup.md) -which gives instructions for installing Java 8 using `cs`. - -#### Error when running the grading tests - -The grading tests (run with `grading:test` or on gitlab) will not work -at all until this part of the assignment is complete, if you get an error like -this: -```scala -==> X codecs.CodecsSuite.initializationError 0.002s java.lang.NoClassDefFoundError: Lcodecs/EncoderInstances$given_Encoder_Boolean$; -``` -It means you haven't defined one of the given instance mentioned above (make -sure you define them as anonymous instance, so `given Encoder[Boolean]`, not -`given booleanEncoder as Encoder[Boolean]`, this is required for our grading -tests to work). - -### Derived Codecs - -The next step consists in implementing a codec for lists of elements of type `A`, given -a codec for type `A`. The encoder instance for lists is already implemented. Fill in -the definition of the corresponding decoder. - -Once you have defined the encoder and decoder for lists, you should be able to -**summon** them for any list containing elements that can be encoded and decoded. -You can try, for instance, to evaluate the following expressions in the REPL: - -~~~ -summon[Encoder[List[Int]]] -summon[Decoder[List[Boolean]]] -~~~ - -### JSON Object Codecs - -Next, implement codecs for JSON objects. The approach consists in defining codecs -for JSON objects having a single field, and then combining such codecs to handle -JSON objects with multiple fields. - -For example, consider the following JSON object with one field `x`: - -~~~ -{ - "x": 1 -} -~~~ - -An encoder for this JSON object can be defined by using the provided -`ObjectEncoder.field` operation, which takes the field name as a parameter and -an implicit `Encoder` for the field value: - -~~~ -val xField = ObjectEncoder.field[Int]("x") -~~~ - -Here is an example of use of the `xField` encoder: - -~~~ -scala> xField.encode(42) -val res0: codecs.Json.Obj = Obj(Map(x -> Num(42))) -~~~ - -Last, to define an encoder producing a JSON object with more than one field, you can -combine two `ObjectEncoder` instances with the `zip` operation. This operation -returns an `ObjectEncoder` producing a JSON object with the fields of the two -combined encoders. For instance, you can define an encoder producing an object -with two fields `x` and `y` as follows: - -~~~ -val pointEncoder: ObjectEncoder[(Int, Int)] = - val xField = ObjectEncoder.field[Int]("x") - val yField = ObjectEncoder.field[Int]("y") - xField.zip(yField) -~~~ - -Implement a `Decoder.field` operation corresponding to the `ObjectEncoder.field` -operation. - -### Codecs for Case Classes - -The `zip` operation mentioned in the previous section only returns codecs for tuples. -It would be more convenient to work with high-level data types (for instance, a -`Point` data type, rather than a pair of coordinates). - -Both encoders and decoders have a `transform` operation that can be used to transform -the type `Json` values are encoded from, or decoded to, respectively. You can -see in the assignment how it is used to define a given `Encoder[Person]`. - -Define a corresponding `Decoder[Person]`, and then define a given instance of `Encoder[Contacts]` -and a given instance of `Decoder[Contacts]`. - -## Bonus Questions - -- Can you **explicitly** write the value inferred by the compiler when it summons - the `Encoder[List[Person]]`? -- Would it be possible to define codecs for optional fields? -- Would it be possible to define a function that provides a given instance of - `Encoder[Option[A]]` for any type `A` for which there is a given instance of `Encoder[A]` - (like we do with `Encoder[List[A]]`)? -- Would it be possible to define codecs for sealed traits in addition to case - classes? - -[JSON]:https://www.json.org diff --git a/labs/lab-9.md b/labs/lab-9.md deleted file mode 100644 index 03d1c9c5a4ed0225d56dcb3f99668364efc0ec0c..0000000000000000000000000000000000000000 --- a/labs/lab-9.md +++ /dev/null @@ -1,144 +0,0 @@ -# Lab 9: Recursive Language (interpreter) - -You can use the following commands to make a fresh clone of your repository: - -```shell -git clone -b interpreter git@gitlab.epfl.ch:lamp/students-repositories-fall-2020/cs210-GASPAR.git cs210-interpreter -cd cs210-interpreter -``` - -You can always refer to: - * [the example guide](https://gitlab.epfl.ch/lamp/cs210/blob/master/labs/example-lab.md) on the development workflow. - * [this guide](https://gitlab.epfl.ch/lamp/cs210/blob/master/labs/grading-and-submission.md) for details on the submission system. - **Make sure to submit your assignment before the deadline written in [README.md](/README.md)** - * [The documentation of the Scala standard library](https://www.scala-lang.org/files/archive/api/2.13.3) - * [The documentation of the Java standard - library](https://docs.oracle.com/en/java/javase/15/docs/api/index.html) - - -## Overview - -The goal of this final assignment is to extend the simple programming language presented in the lecture with a list-like datatype. Recall the datatype of expressions: - -~~~scala -enum Expr: - case Constant(value: Int) - case Name(name: String) - case BinOp(op: BinOps, arg1: Expr, arg2: Expr) - case IfNonzero(cond: Expr, caseTrue: Expr, caseFalse: Expr) - case Call(function: Expr, arg: Expr) - case Fun(param: String, body: Expr) -~~~ - -We extend this datatype with 3 extra cases: - -~~~scala -enum Expr: - [...] - case Empty - case Cons(head: Expr, tail: Expr) - case Match(scrutinee: Expr, caseEmpty: Expr, - headName: String, tailName: String, caseCons: Expr) -~~~ - -`Empty` corresponds to the empty list, also known as `Nil` in Scala. `Cons` *cons*-tructs memory objects holding two values, called head and tail. It is our language analog to Scala's `::`. Finally, `Match` is a pattern matching expression over lists. It takes 5 arguments: - -- `scrutinee`, the expression that's being matched over -- `caseEmpty `, the result of the pattern matching when `scrutinee` evaluates to `Empty` -- `caseCons`, the result of the pattern matching when `scrutinee` evaluates to `Cons` -- `headName` and `tailName` are names that *bind* the two values of `Cons` in `caseCons`. - -`Match` can be also explained by analogy to Scala's pattern matching. The 5 arguments described above correspond to the placeholders in the following expression: - -~~~scala -$scrutinee match - case Nil => $caseEmpty - case $headName :: $tailName => $caseCons -~~~ - -## Running the interpreter - -You can use (and update!) the `main` method at the end of the `RecursiveLanguage` object to execute programs using the interpreter (call `run` from sbt to run the program). To execute programs, use eval (or tracingEval, the debugging with logging enabled). These functions takes two arguments, the expression that's being interpreted, and a set of top-level definitions given to the interpreter. - -## Examples functions - -Once you completed the recitation session, transcribe your implementations of `gcd`, `map` and `foldLeft` into the lab. These definitions should go into `val definitions` at the end of `RecursiveLanguage.scala`. You can see your `gcd` implementation in action by changing the body of the `main` method to: - -~~~scala -def main(args: Array[String]): Unit = - tracingEval(Call(Call(N("gcd"), C(6)), C(9)), definitions) -~~~ - -Execute the interpreter using `run` from sbt. With the reference implementation, the trace looks as follows: - -~~~ -gcd(6)(9) -| gcd(6) -| FUN: (a => (b => (if b then gcd(b)((% a b)) else a))) ARG: 6 -| (b => (if b then gcd(b)((% 6 b)) else 6)) -| +--> (b => (if b then gcd(b)((% 6 b)) else 6)) -[...] -+--> 3 - ~~> Constant(3) -~~~ - -A correct implementation of `gcd` should already give you a passing test (it doesn't use any list constructs!). Can will be able to play around with your `map` and `foldLeft` implementations once you complete the assignment. - -Update the `show` function to support pretty-printing of lists. We suggest (but do not test) that you mirror the Scala syntax for list constructors (`1 :: 2 :: 3 :: Nil`) and for pattern matching. - -Also implement `foldRight`. In the reference solution, it pretty prints as follows: - -~~~scala -def foldRight = - (ls => (z => (fold => ls match { case nil => z; case x :: xs => fold(x)(foldRight(xs)(z)(fold)) }))) -~~~ - -You might have noticed while compiling the project the presence of several warnings about pattern matching exhaustivity. These warnings appeared when we extended `enum Expr`, and should be all gone once you completed your implementation. - -## Evaluation - -Update the `eval` function to support `Empty`, `Cons` and `Match`. `Empty` should evaluate to itself. Likewise, `Cons` should evaluate to a `Cons` with evaluated arguments. `Match` should first evaluate its `scrutinee`, then either reduce to `caseEmpty`, reduce to `caseCons` or raise an error if the `scrutinee` is not a list. - -Adding cases in the `eval` function should be sufficient to evaluate `Match` expressions in isolation (see `evalTests` in `RecursiveLanguageSuite.scala`), but extra work is needed to support `Match` used into functions and nested `Match`-s. - -### Substitution - -Substitution is implemented using 3 functions, `subst`, `freeVars` and `alphaConvert`. That complexity is need to solve the variable capture problem, which we will explain in this section using an example. Suppose the following definitions for factorial and twice (in Scala): - -~~~scala -val fact: - Int => Int = - n => if n == 0 then 1 else n * fact(n - 1) - -val twice: - (Int => Int) => Int => Int = - f => fact => f(f(fact)) -~~~ - -Note the name conflict between the second argument of `twice` and the fact function. Let's evaluate `twice(fact)(3)` using naive substitution: - -~~~scala -twice(fact)(3) ---> twice(n => if n == 0 then 1 else n * fact(n - 1))(3) ---> fact => f(f(fact)) [substitute f by n => if n == 0 then 1 else n * fact(n - 1)] ---> *BOOM* -~~~ - -The naive substitution would break with the example above because of the recursion in factorial. A simple syntactic replacement of `f` would *change* the meaning of `fact` from a reference to the `fact` function (the recursive call) to a reference to the second argument of twice. - -To solve this problem, substitution needs to carefully avoid such accidental capture. To detect name clashes, we first compute the set of names "at-risk", which we call *free variables*. When substituting `f` by `n => if n == 0 then 1 else n * fact(n - 1)` the name at risk is `fact`. The variable `fact` is called a *free variable* in that example because it doesn't have local meaning, it's "free" when we consider that expression in isolation. - -Whenever we detect a name clash, we can work around it by renaming the problematic variable. This operation is traditionally called alpha conversion. The correct version of substitution would evaluate `twice(fact)(3)` as follows: - -~~~scala -twice(fact)(3) ---> twice(n => if n == 0 then 1 else n * fact(n - 1))(3) ---> (fact => f(f(fact)))(3) [rename fact to fact', then substitute f by n => if n == 0 then 1 else n * fact(n - 1)] ---> (fact' => f(f(fact')))(3) [substitute f by n => if n == 0 then 1 else n * fact(n - 1)] ---> [...] ---> 720 -~~~ - -Looking back at pattern matching expression, we can see that the two binders of a `Match` suffer from the same variable capturing problem than function. Indeed, given an expression of shape `(h :: t) match { case x :: xs => body }`, when we substitute `x` by `h` and `xs` by `t` in `body`, we need to avoid changing the meaning of free variables in `h` and `t`. - -Update `freeVars` and `alphaConvert` to with cases for `Empty`, `Cons` and `Match`. Have a look at the corresponding unit tests, `freeVarsTests` and `alphaConvertTests` in `RecursiveLanguageSuite.scala`. Finally, update `subst` with capture avoiding substitution for pattern matching. Note that `subst` is only tested using integration tests, so make sure that you have all other tests passing before starting to implement that last function. Also note that this assignment doesn't have any black-box tests (no need to run `grading:test`, `test` will run all the tests). diff --git a/slides/.gitkeep b/slides/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/slides/progfun1-1-1.pdf b/slides/progfun1-1-1.pdf deleted file mode 100644 index 98481344e094fe133de77685c0f61d094252f78e..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-1-1.pdf and /dev/null differ diff --git a/slides/progfun1-1-2.pdf b/slides/progfun1-1-2.pdf deleted file mode 100644 index 395946cdc8020ad3b92c4d99437ea9d895d8b7a8..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-1-2.pdf and /dev/null differ diff --git a/slides/progfun1-1-3.pdf b/slides/progfun1-1-3.pdf deleted file mode 100644 index cd34473e03d7d45ecbdf559e70167d03eedad35b..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-1-3.pdf and /dev/null differ diff --git a/slides/progfun1-1-4.pdf b/slides/progfun1-1-4.pdf deleted file mode 100644 index 1cc8c63808e1c95a120269df670309ce735c8f2c..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-1-4.pdf and /dev/null differ diff --git a/slides/progfun1-1-5.pdf b/slides/progfun1-1-5.pdf deleted file mode 100644 index 3bb114312992fb584d92fda18398991ad93f3eed..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-1-5.pdf and /dev/null differ diff --git a/slides/progfun1-1-6.pdf b/slides/progfun1-1-6.pdf deleted file mode 100644 index 7eb20bc6653546233732affd5971a7f562426995..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-1-6.pdf and /dev/null differ diff --git a/slides/progfun1-1-7.pdf b/slides/progfun1-1-7.pdf deleted file mode 100644 index 4504698f5567c8d8182c714dc3417a56d42b6347..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-1-7.pdf and /dev/null differ diff --git a/slides/progfun1-2-1.pdf b/slides/progfun1-2-1.pdf deleted file mode 100644 index a9b805473f9424664fe8ee574a72a6fda2cde231..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-2-1.pdf and /dev/null differ diff --git a/slides/progfun1-2-2.pdf b/slides/progfun1-2-2.pdf deleted file mode 100644 index d0c813c90862b3da520bfa08629e7135e13e7f4b..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-2-2.pdf and /dev/null differ diff --git a/slides/progfun1-2-3.pdf b/slides/progfun1-2-3.pdf deleted file mode 100644 index 1c0b3b88c21ef6934e20d4883d75db056f3f3cec..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-2-3.pdf and /dev/null differ diff --git a/slides/progfun1-2-4.pdf b/slides/progfun1-2-4.pdf deleted file mode 100644 index baeddc7e046d5c39fda22897de2a581ae1ebfa0d..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-2-4.pdf and /dev/null differ diff --git a/slides/progfun1-2-5.pdf b/slides/progfun1-2-5.pdf deleted file mode 100644 index 03ae403e71c4bdea1abd36de5e71e7d9189a2726..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-2-5.pdf and /dev/null differ diff --git a/slides/progfun1-2-6.pdf b/slides/progfun1-2-6.pdf deleted file mode 100644 index e29db28de87cdc8a519804788cc5688afae1849d..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-2-6.pdf and /dev/null differ diff --git a/slides/progfun1-2-7.pdf b/slides/progfun1-2-7.pdf deleted file mode 100644 index a1ce24776aadf7a9cde5173c123157b3d31702d7..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-2-7.pdf and /dev/null differ diff --git a/slides/progfun1-3-1.pdf b/slides/progfun1-3-1.pdf deleted file mode 100644 index c8f0670988280137ac4e241d6afeb379aad5ce76..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-3-1.pdf and /dev/null differ diff --git a/slides/progfun1-3-2.pdf b/slides/progfun1-3-2.pdf deleted file mode 100644 index d677bc47d5ce82bd390385bb418b51d1fdbb3548..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-3-2.pdf and /dev/null differ diff --git a/slides/progfun1-3-3.pdf b/slides/progfun1-3-3.pdf deleted file mode 100644 index 7b7b87e467add2a62fc7423687cbc07c09b4dcb7..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-3-3.pdf and /dev/null differ diff --git a/slides/progfun1-3-4.pdf b/slides/progfun1-3-4.pdf deleted file mode 100644 index a97b7dfa95d77761d406c8ddff790cbcda51459b..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-3-4.pdf and /dev/null differ diff --git a/slides/progfun1-3-5.pdf b/slides/progfun1-3-5.pdf deleted file mode 100644 index 2eeef74a0d032807735e235f8b71854b27a9b1de..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-3-5.pdf and /dev/null differ diff --git a/slides/progfun1-4-1.pdf b/slides/progfun1-4-1.pdf deleted file mode 100644 index f43750120d919459e4ed51a3efbdccd125ee9470..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-4-1.pdf and /dev/null differ diff --git a/slides/progfun1-4-2.pdf b/slides/progfun1-4-2.pdf deleted file mode 100644 index 797edafc2443991c539cf3c82fa397f5c2e43380..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-4-2.pdf and /dev/null differ diff --git a/slides/progfun1-4-3.pdf b/slides/progfun1-4-3.pdf deleted file mode 100644 index 5de94759c94d23bba91ae9b1d30221f1d76e5eea..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-4-3.pdf and /dev/null differ diff --git a/slides/progfun1-4-4.pdf b/slides/progfun1-4-4.pdf deleted file mode 100644 index 308fc36450fc62a278c5a61853bd4f1344124feb..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-4-4.pdf and /dev/null differ diff --git a/slides/progfun1-4-5.pdf b/slides/progfun1-4-5.pdf deleted file mode 100644 index 56ccbd0d6720258a1fd89b7e0a380e39217958ac..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-4-5.pdf and /dev/null differ diff --git a/slides/progfun1-4-6.pdf b/slides/progfun1-4-6.pdf deleted file mode 100644 index 4e241954decdbe8f8837844772b7332d8271a9ec..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-4-6.pdf and /dev/null differ diff --git a/slides/progfun1-5-1.pdf b/slides/progfun1-5-1.pdf deleted file mode 100644 index 15bc68535445981f89e7f0e72eaf15fefc2fb6e2..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-5-1.pdf and /dev/null differ diff --git a/slides/progfun1-5-2.pdf b/slides/progfun1-5-2.pdf deleted file mode 100644 index 8225a74e7a5a08ab3668177cfaa575dd6a8d8fcc..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-5-2.pdf and /dev/null differ diff --git a/slides/progfun1-5-3.pdf b/slides/progfun1-5-3.pdf deleted file mode 100644 index 43f724a66eb2122a4c67190d2b10bcf98f7579e8..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-5-3.pdf and /dev/null differ diff --git a/slides/progfun1-5-4.pdf b/slides/progfun1-5-4.pdf deleted file mode 100644 index e936acaab3a087ef1f56b5c6aa20e18d0a8f910c..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-5-4.pdf and /dev/null differ diff --git a/slides/progfun1-5-5.pdf b/slides/progfun1-5-5.pdf deleted file mode 100644 index aa52251fafaa854cb1e7eea12ad1919239de0b82..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-5-5.pdf and /dev/null differ diff --git a/slides/progfun1-5-6.pdf b/slides/progfun1-5-6.pdf deleted file mode 100644 index 1d5dff712b0fbc8fca4cb140f01253625e72e30b..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-5-6.pdf and /dev/null differ diff --git a/slides/progfun1-6-1.pdf b/slides/progfun1-6-1.pdf deleted file mode 100644 index 4bb31e78c6e73736542b4c361c636b06067de37c..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-6-1.pdf and /dev/null differ diff --git a/slides/progfun1-6-2.pdf b/slides/progfun1-6-2.pdf deleted file mode 100644 index 75774eae2aa9c4b887fbe7ba0c140b98b010d2ad..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-6-2.pdf and /dev/null differ diff --git a/slides/progfun1-6-3.pdf b/slides/progfun1-6-3.pdf deleted file mode 100644 index d89c42afcca8565db019b747e48e97f1629c68cb..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-6-3.pdf and /dev/null differ diff --git a/slides/progfun1-6-4.pdf b/slides/progfun1-6-4.pdf deleted file mode 100644 index cf228d6081cc669ad9ee9ab16213785bc12c7cad..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-6-4.pdf and /dev/null differ diff --git a/slides/progfun1-6-5.pdf b/slides/progfun1-6-5.pdf deleted file mode 100644 index 88a426a27c46384e17c3282145c61318244edfb9..0000000000000000000000000000000000000000 Binary files a/slides/progfun1-6-5.pdf and /dev/null differ diff --git a/slides/progfun2-1-1.pdf b/slides/progfun2-1-1.pdf deleted file mode 100644 index 47dd651f9180ae0e61d017d3ac5cefc168016edd..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-1-1.pdf and /dev/null differ diff --git a/slides/progfun2-1-2.pdf b/slides/progfun2-1-2.pdf deleted file mode 100644 index 3f02bea763edecbde0aadda2f92a5716f8e44a12..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-1-2.pdf and /dev/null differ diff --git a/slides/progfun2-1-3.pdf b/slides/progfun2-1-3.pdf deleted file mode 100644 index 501df96ed202ba9c6b3584400807d776a8722ecb..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-1-3.pdf and /dev/null differ diff --git a/slides/progfun2-1-4.pdf b/slides/progfun2-1-4.pdf deleted file mode 100644 index 8224577ce15a1ca885f300aa0e5b3724692f1873..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-1-4.pdf and /dev/null differ diff --git a/slides/progfun2-1-5.pdf b/slides/progfun2-1-5.pdf deleted file mode 100644 index 743871d2fd8fccb41b82f7461bf3b37ff9356c70..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-1-5.pdf and /dev/null differ diff --git a/slides/progfun2-1-6.pdf b/slides/progfun2-1-6.pdf deleted file mode 100644 index 34315477dffabaa34a263e5f422d96f6ac4f607d..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-1-6.pdf and /dev/null differ diff --git a/slides/progfun2-2-1.pdf b/slides/progfun2-2-1.pdf deleted file mode 100644 index af2e74fc1fb49e9aa0a272c99d3f0d4dde34d5a4..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-2-1.pdf and /dev/null differ diff --git a/slides/progfun2-2-2.pdf b/slides/progfun2-2-2.pdf deleted file mode 100644 index 85b264622533023003515da26de946dd7d6cdbf1..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-2-2.pdf and /dev/null differ diff --git a/slides/progfun2-2-3.pdf b/slides/progfun2-2-3.pdf deleted file mode 100644 index 6a37712d9dd4c7e3bf3dfd59902f3cc83dcb243b..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-2-3.pdf and /dev/null differ diff --git a/slides/progfun2-2-4.pdf b/slides/progfun2-2-4.pdf deleted file mode 100644 index eb169ed2effe51d7a1660d20e695b6a58c5f71f9..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-2-4.pdf and /dev/null differ diff --git a/slides/progfun2-2-5.pdf b/slides/progfun2-2-5.pdf deleted file mode 100644 index 8055c0b09987d4aa6082e3abed56d7413249411d..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-2-5.pdf and /dev/null differ diff --git a/slides/progfun2-3-1.pdf b/slides/progfun2-3-1.pdf deleted file mode 100644 index 9d3037725d8d89b039dcf30ab954fb0a2d736a82..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-3-1.pdf and /dev/null differ diff --git a/slides/progfun2-3-2.pdf b/slides/progfun2-3-2.pdf deleted file mode 100644 index f6dfcbf75cfff4f2b899f73ae91829473ea36369..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-3-2.pdf and /dev/null differ diff --git a/slides/progfun2-3-3.pdf b/slides/progfun2-3-3.pdf deleted file mode 100644 index ae80b3b2c071a5c13204f0461c6c10ed2efcd679..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-3-3.pdf and /dev/null differ diff --git a/slides/progfun2-3-4.pdf b/slides/progfun2-3-4.pdf deleted file mode 100644 index d6b4f0e005ff88ecf84ba16ad1fdccd6deb66922..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-3-4.pdf and /dev/null differ diff --git a/slides/progfun2-3-5.pdf b/slides/progfun2-3-5.pdf deleted file mode 100644 index 4659d28e61e163a99b07877a08a92d2f61b86e84..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-3-5.pdf and /dev/null differ diff --git a/slides/progfun2-3-6.pdf b/slides/progfun2-3-6.pdf deleted file mode 100644 index e800975f5a02a02a786f162a6e056c3292ac5747..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-3-6.pdf and /dev/null differ diff --git a/slides/progfun2-4-1.pdf b/slides/progfun2-4-1.pdf deleted file mode 100644 index a0a24d91135d74d7548318ef6f75b5f89973d7db..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-4-1.pdf and /dev/null differ diff --git a/slides/progfun2-4-2.pdf b/slides/progfun2-4-2.pdf deleted file mode 100644 index 9bd9be6e6f7048353c7618b202db62d45eca4001..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-4-2.pdf and /dev/null differ diff --git a/slides/progfun2-4-3.pdf b/slides/progfun2-4-3.pdf deleted file mode 100644 index 2a75d0a05d90deb6670fdaebf7b1c1ff3c4245cd..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-4-3.pdf and /dev/null differ diff --git a/slides/progfun2-4-4.pdf b/slides/progfun2-4-4.pdf deleted file mode 100644 index 49f9e2984e8e578508a36e2162eb1166ffa7d04f..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-4-4.pdf and /dev/null differ diff --git a/slides/progfun2-4-5.pdf b/slides/progfun2-4-5.pdf deleted file mode 100644 index 38777e761c58a9538e0e8d929917579df991ba0e..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-4-5.pdf and /dev/null differ diff --git a/slides/progfun2-4-6.pdf b/slides/progfun2-4-6.pdf deleted file mode 100644 index 9c641eecaf77ce9d56e9824fd489ff4b384e5b15..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-4-6.pdf and /dev/null differ diff --git a/slides/progfun2-5-1.pdf b/slides/progfun2-5-1.pdf deleted file mode 100644 index daa982a06dea85bc20b6be978f769951e307c0e4..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-5-1.pdf and /dev/null differ diff --git a/slides/progfun2-5-2.pdf b/slides/progfun2-5-2.pdf deleted file mode 100644 index 00ea955463cebf4b4395c923c24db8ffdd974e5a..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-5-2.pdf and /dev/null differ diff --git a/slides/progfun2-5-3.pdf b/slides/progfun2-5-3.pdf deleted file mode 100644 index d04470a3a331d6a4e3289adae5263f8d3d8f2071..0000000000000000000000000000000000000000 Binary files a/slides/progfun2-5-3.pdf and /dev/null differ diff --git a/slides/progfun3-1-1.pdf b/slides/progfun3-1-1.pdf deleted file mode 100644 index 71428c2b6fb704ec44556fb93cd813eeff101e35..0000000000000000000000000000000000000000 Binary files a/slides/progfun3-1-1.pdf and /dev/null differ diff --git a/slides/progfun3-1-2.pdf b/slides/progfun3-1-2.pdf deleted file mode 100644 index aa1682ed12d57b66cba91cb561d7b0ea24c88ced..0000000000000000000000000000000000000000 Binary files a/slides/progfun3-1-2.pdf and /dev/null differ diff --git a/slides/progfun3-1-3.pdf b/slides/progfun3-1-3.pdf deleted file mode 100644 index a0e6db3b4c58878336dc1f1491295588647e3a17..0000000000000000000000000000000000000000 Binary files a/slides/progfun3-1-3.pdf and /dev/null differ diff --git a/slides/progfun3-1-4.pdf b/slides/progfun3-1-4.pdf deleted file mode 100644 index d021448df01e29f902d647d20dfd5047d6104f7b..0000000000000000000000000000000000000000 Binary files a/slides/progfun3-1-4.pdf and /dev/null differ diff --git a/slides/progfun3-1-5.pdf b/slides/progfun3-1-5.pdf deleted file mode 100644 index d2748d660c8ebe9be69fb9e24c51ff1b263bb51b..0000000000000000000000000000000000000000 Binary files a/slides/progfun3-1-5.pdf and /dev/null differ diff --git a/slides/progfun3-1-6.pdf b/slides/progfun3-1-6.pdf deleted file mode 100644 index ac542185ff9cebb4f7724d425a96be6b8cc0c3b4..0000000000000000000000000000000000000000 Binary files a/slides/progfun3-1-6.pdf and /dev/null differ diff --git a/slides/progfun3-1-7.pdf b/slides/progfun3-1-7.pdf deleted file mode 100644 index 217b170a60fc93958ee8e564914b0dba1c697b24..0000000000000000000000000000000000000000 Binary files a/slides/progfun3-1-7.pdf and /dev/null differ