Differences
This shows you the differences between two versions of the page.
Next revision | Previous revision | ||
pp:2023:scala:l03 [2023/03/08 13:48] andrei.cirpici created |
pp:2023:scala:l03 [2023/04/02 17:28] (current) alexandra.udrescu01 |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Lab 3. High order functions ====== | + | ====== Lab 3. Lists in Scala ====== |
Objectives: | Objectives: | ||
- | * implement and use **higher-order** functions. A **higher-order** function takes other functions as parameter or returns them | + | * get familiar with **pattern matching** lists, as well as common list operations from Scala and how they work |
- | * implement **curry** and **uncurry** functions, and how they should be properly used (review lecture). | + | * get familiar with common **higher-order functions** over lists (partition, map, foldRight, foldLeft, filter) |
- | ** Create a new Scala worksheet to write your solutions ** | + | ==== 3.1. Common list operations ==== |
- | ===== 3.1 Intro. Functions as parameters ===== | + | **3.1.1.** Write a function which returns true if a list of integers has at least k elements. Use patterns. Write a second function which returns true if the list has at least k elements that satisfy a predicate. |
- | **1.1.1** Write a function ''apply'' that takes an integer and return the result of the applied function on the given integer. Start from the code stub below: | + | |
<code scala> | <code scala> | ||
- | def apply(n: Int, f: Int => Int): Int = { | + | def atLeastk(k: Int, l: List[Int]): Boolean = |
- | ??? | + | if (k == 0) ??? |
- | } | + | else ??? |
+ | } | ||
+ | |||
+ | def atLeastkPred(pred: Int => Boolean)(k: Int, l: List[Int]): Boolean = ??? | ||
</code> | </code> | ||
- | <hidden> | + | |
- | Solution: | + | **3.1.2.** Write a function which returns the first ''n'' elements from a given list. The function should not be implemented as tail-recursive. |
<code scala> | <code scala> | ||
- | def apply(n: Int, f: Int => Int): Int = { | + | def take(n: Int, l: List[Int]): List[Int] = ??? |
- | f(n) | + | //take(3,List(1,2,3,4,5)) = List(1,2,3) |
- | } | + | |
</code> | </code> | ||
- | </hidden> | ||
- | **1.1.2** Write a function ''doubler'' that returns a function that doubles the input it receives (an integer). Start from the code stub below: | + | **3.1.3.** Write a function which //drops// the first ''n'' elements from a given list. |
<code scala> | <code scala> | ||
- | def doubler(): Int => Int = { | + | def drop(n: Int, l: List[Int]): List[Int] = ??? |
- | ??? | + | //drop(3,List(1,2,3,4,5)) = List(4,5) |
- | } | + | |
</code> | </code> | ||
- | <hidden> | + | |
- | Solution: | + | **3.1.4.** Write a function which takes a predicate ''p: Int => Boolean'', a list ''l'' and returns a sublist of ''l'' containing those elements for which ''p'' is true. The function should be **curried**. |
<code scala> | <code scala> | ||
- | def doubler(): Int => Int = { | + | def takeP(p: Int => Boolean)(l: List[Int]): List[Int] = ??? |
- | x => 2*x | + | //takeP(_%2 == 0)(List(1,2,3,4,5,6)) = List(2,4,6) |
- | } | + | |
</code> | </code> | ||
- | or | + | |
+ | **3.1.5.** Write a function which uses a predicate to partition (split) a list. | ||
<code scala> | <code scala> | ||
- | def doubler(): Int => Int = { | + | def part(p: Int => Boolean)(l: List[Int]): (List[Int], List[Int]) = ??? |
- | def double(x: Int): Int = { | + | // part(_%2 == 0)(List(1,2,3,4,5,6)) = (List(2,4,6),List(1,3,5)) |
- | 2*x | + | |
- | } | + | |
- | double | + | |
- | } | + | |
</code> | </code> | ||
- | </hidden> | ||
- | ===== 3.2 Custom high order functions ===== | + | ==== 3.2. String processing ==== |
- | **3.2.1** Define the function ''foldWith'' which uses an operation ''op'' to reduce a range of integers to a value. For instance, given that ''op'' is addition (+), the result of folding the range 1 to 3 will be 1+2+3=6. ''foldWith'' should be curried (it will take the operation and return another function which expects the bounds). | + | |
+ | In what follows, we shall encode a String as a list of characters, using the type defined below: | ||
<code scala> | <code scala> | ||
- | def foldWith (op: (Int,Int) => Int)(start: Int, stop: Int): Int = { | + | type Str = List[Char] |
- | def tail_fold(crt: Int, acc: Int): Int = ??? | + | |
- | ?? | + | |
- | } | + | |
</code> | </code> | ||
- | <hidden> | + | Add this type alias to your code before solving the following exercises. |
- | Solution: | + | |
+ | The following is an input test. You can add more examples to it: | ||
<code scala> | <code scala> | ||
- | def foldWith (op: (Int,Int) => Int)(start: Int, stop: Int): Int = { | + | val l: List[Str] = List("matei@gmail.com", "mihai@gmail.com", "tEst@mail.com", "email@email.com", "short@ax.ro").map(x => x.toList) |
- | def tail_fold(crt: Int, acc: Int): Int = { | + | |
- | if crt == stop then | + | |
- | acc | + | |
- | else | + | |
- | tail_fold(crt + 1, op(acc, crt)) | + | |
- | } | + | |
- | tail_fold(start + 1, start) | + | |
- | } | + | |
</code> | </code> | ||
- | </hidden> | ||
- | **3.2.2** Define the function ''foldConditional'' which extends ''foldWith'' by also adding a predicate ''p: Int => Int''. ''foldConditional'' will reduce only those elements of a range which satisfy the predicate. | + | Use ''map'', ''foldr''/''foldl'', instead of recursive functions. |
+ | |||
+ | **3.2.1.** Remove uppercases from emails. (Do **not** use recursion). Use the Internet to find the appropriate character function. | ||
<code scala> | <code scala> | ||
- | def foldConditional(op: (Int,Int) => Int, p: Int => Boolean)(start: Int, stop: Int): Int = ??? | + | def remUpper(list: List[Str]): List[Str] = ??? |
</code> | </code> | ||
- | <hidden> | + | |
- | Solution: | + | **3.2.2.** Write a function which removes emails longer than a given size. Try to think of two ways to implement this using already defined functions (do not define your own auxiliary functions). |
<code scala> | <code scala> | ||
- | def foldConditional(op: (Int,Int) => Int, p: Int => Boolean)(start: Int, stop: Int): Int = { | + | def longer(k: Int, list: List[Str]): List[Str] = ??? |
- | def tail_fold(crt: Int, acc: Int): Int = { | + | </code> |
- | if crt == stop then { | + | |
- | acc | + | |
- | } | + | |
- | else { | + | |
- | if p(crt) then { | + | |
- | tail_fold(crt + 1, op(acc, crt)) | + | |
- | } | + | |
- | else { | + | |
- | tail_fold(crt + 1, acc) | + | |
- | } | + | |
- | } | + | |
- | } | + | |
- | if p(start) then { | + | **3.2.3.** Count the number of emails longer than k characters. Use ''foldRight''. |
- | tail_fold(start + 1, start) | + | |
- | } | + | <code scala> |
- | else { | + | def howMany(k: Int)(list: List[Str]): Int = ??? |
- | if start < stop then { | + | |
- | foldConditional(op, p)(start + 1, stop) | + | |
- | } | + | |
- | else { | + | |
- | 0 | + | |
- | } | + | |
- | } | + | |
- | } | + | |
</code> | </code> | ||
- | </hidden> | + | |
- | **3.2.3** Write a function ''foldMap'' which takes values $math[a_1, a_2, \ldots, a_k] from a range and computes $math[f(a_1)\;op\;f(a_2)\;op\;\ldots f(a_k)]. | + | **3.2.4.** Split the list between first names and email domains. What ingredients (auxiliary functions) are necessary? Use either a fold or a tail-recursive function in your implementation. |
- | Use the ''apply'' and ''foldWith'' methods | + | |
<code scala> | <code scala> | ||
- | def foldMap(op: (Int,Int) => Int, f: Int => Int)(start: Int, stop: Int): Int = ??? | + | def namesEmails(list: List[Str]): List[(Str, Str)] = ??? |
</code> | </code> | ||
- | <hidden> | + | |
- | Solution: | + | **3.2.5.** Identify the list of the employed domain names (e.g. ''gmail.com''). Remove duplicates. Use no recursion. |
<code scala> | <code scala> | ||
- | def foldMap(op: (Int,Int) => Int, f: Int => Int)(start: Int, stop: Int): Int = { | + | def domains(l: List[Str]): List[Str] = ??? |
- | def tail_aux(crt: Int, acc: Int): Int = { | + | |
- | if crt == stop then { | + | |
- | acc | + | |
- | } | + | |
- | else { | + | |
- | tail_aux(crt + 1, op(acc, f(crt))) | + | |
- | } | + | |
- | } | + | |
- | tail_aux(start + 1, f(start)) | + | |
- | } | + | |
</code> | </code> | ||
- | </hidden> | ||
+ | **(!) 3.2.6.** In some previous exercise you have, most likely, used already defined function to split the emails. Try implementing a split function using ''foldRight''. Try to figure out what the accumulator should do. | ||
+ | <code scala> | ||
+ | def mySplit(l: Str): List[Str] = ??? | ||
+ | </code> | ||
- | **3.2.4** Write a function which computes $math[1 + 2^2 + 3^2 + \ldots + (n-1)^2 + n^2] using ''foldMap''. | + | **3.2.7.** Generalize the former function for any given character. Use it to implement a function that return the domains without the dot (ex. ''gmail''). |
<code scala> | <code scala> | ||
- | def sumSquares(n: Int): Int = ??? | + | def domain(list: List[Str]): List[Str] = ??? |
</code> | </code> | ||
- | <hidden> | + | |
- | Solution: | + | |
+ | |||
+ | |||
+ | ==== 3.3. Gradebooks ==== | ||
+ | More general implementations of ''taken'', ''dropn'' and ''part'' are already implemented in Scala and can be used as member functions of lists. Examples are shown below: | ||
<code scala> | <code scala> | ||
- | def sumSquares(n: Int): Int = foldMap(_+_, x => x*x)(1, n) | + | val l = List(1,2,3,4,5,6,7,8,9) |
+ | l.take(3) | ||
+ | l.drop(3) | ||
+ | l.partition(_%2 == 0) | ||
</code> | </code> | ||
- | or | + | |
+ | In what follows, we shall encode a gradebook as a list of pairs ''(<name>,<grade>)'', where ''<name>'' is a String and ''<grade>'' is an Int. Example: | ||
+ | <code scala> | ||
+ | val gradebook: List[(Str, Int)] = List((List('G'),3), (List('F'), 10), (List('M'),6), (List('P'),4)) | ||
+ | </code> | ||
+ | |||
+ | To make the type signatures more legible, we can introduce type aliases in Scala: | ||
<code scala> | <code scala> | ||
- | def sumSquares(n: Int): Int = foldMap((x, y) => x + y, x => x*x)(1, n) | + | type Gradebook = List[(Str,Int)] //the type Gradebook now refers to a list of pairs of String and Int |
</code> | </code> | ||
- | </hidden> | + | Add this type alias to your code before solving the following exercises. |
- | **3.2.5** Write a function ''hasDivisor'' which checks if a range contains a multiple of k. Use ''foldMap'' and choose ''f'' carefully. | + | **3.3.1.** Write a function which adds one point to all students which have a passing grade (>= 5), and leaves all other grades unchanged. |
<code scala> | <code scala> | ||
- | def hasDivisor(k: Int, start: Int, stop: Int): Boolean = ??? | + | def increment(g: Gradebook): Gradebook = |
+ | g.map(???) | ||
</code> | </code> | ||
- | <hidden> | + | |
- | Solution: | + | **3.3.2.** Find the average grade from a gradebook. You must use ''foldRight''. |
<code scala> | <code scala> | ||
- | def hasDivisor(k: Int, start: Int, stop: Int): Boolean = foldMap(_ & _, _ % k)(start, stop) == 0 | + | def average(g: Gradebook): Double = ??? |
</code> | </code> | ||
- | or | + | |
+ | **3.3.3.** Write a function which takes a gradebook and returns the percentage of failed vs. passed students, as a pair (x,y). | ||
<code scala> | <code scala> | ||
- | def hasDivisor(k: Int, start: Int, stop: Int): Boolean = foldMap((x, y) => x & y, x => x % k)(start, stop) == 0 | + | def percentage(g: Gradebook): (Double,Double) = ??? |
</code> | </code> | ||
- | </hidden> | ||
- | **3.7.** We can compute the sum of an area defined by a function within a range a,b (the integral of that function given the range), using the following recursive scheme: | + | **3.3.4.** Write a function which takes a gradebook and returns the list of names which have passed. Use filter and map from Scala. |
- | * if the range is small enough, we treat f as a line (and the area as a trapeze). It's area is $math[(f(a) + f(b))(b-a)/2]. | + | |
- | * otherwise, we compute the mid of the range, we recursively compute the integral from a to mid and from mid to b, and add-up the result. | + | |
- | + | ||
- | Implement the function ''integrate'' which computes the integral of a function f given a range: | + | |
<code scala> | <code scala> | ||
- | def integrate(f: Double => Double)(start: Double, stop: Double): Double = ??? | + | def pass(g: Gradebook): List[Str] = ??? |
</code> | </code> | ||
- | <hidden> | + | |
- | Solution: | + | **3.3.5.** Implement merge-sort (in ascending order) over gradebooks: |
<code scala> | <code scala> | ||
- | def integrate(f: Double => Double)(start: Double, stop: Double): Double = { | + | def mergeSort(l: Gradebook): Gradebook = { |
- | def aux(crt: Double, acc: Double): Double = { | + | def merge(u: Gradebook, v: Gradebook): Gradebook = ??? |
- | if crt >= stop then | + | ??? |
- | acc | + | |
- | else | + | |
- | aux(crt + 0.01, acc + (f(crt) + f(crt + 0.01))*0.01/2) | + | |
- | } | + | |
- | aux(start, 0) | + | |
} | } | ||
</code> | </code> | ||
- | </hidden> | ||
+ | **3.3.6** Write a function which takes a gradebook and reports all passing students in **descending** order of their grade. | ||
+ | <code scala> | ||
+ | def honorsList(g: Gradebook): List[Str] = ??? | ||
+ | </code> |