Differences
This shows you the differences between two versions of the page.
Next revision | Previous revision | ||
fp2025:lab03 [2025/03/03 00:20] cata_chiru created |
fp2025:lab03 [2025/03/12 15:40] (current) pdmatei |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Lab 3. Lists in Scala ====== | + | ====== Lab 3. Higher-order functions ====== |
Objectives: | Objectives: | ||
- | * get familiar with **pattern matching** lists, as well as common list operations from Scala and how they work | + | * implement and use **higher-order** functions. A **higher-order** function takes other functions as parameter or returns them |
- | * get familiar with common **higher-order functions** over lists (partition, map, foldRight, foldLeft, filter) | + | * implement **curry** and **uncurry** functions, and how they should be properly used (review lecture). |
- | ==== 3.1. Common list operations ==== | + | **3.1.** Define the function ''foldWith'' which uses an operation ''op'', and an initial value ''b'' 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 with ''b = 0'' will be ''( ( 0 + 1 ) + 2 + 3 = 6''. ''foldWith'' should be **curried** (it will take the operation and return another function which expects the bounds). |
- | **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. | ||
<code scala> | <code scala> | ||
- | def atLeastk(k: Int, l: List[Int]): Boolean = { | + | def foldWith (b: Int)(op: (Int,Int) => Int)(start: Int, stop: Int): Int = { |
- | if (k == 0) ??? | + | def tail_fold(crt: Int, acc: Int): Int = ??? |
- | else ??? | + | ?? |
} | } | ||
- | | ||
- | def atLeastkPred(pred: Int => Boolean)(k: Int, l: List[Int]): Boolean = ??? | ||
</code> | </code> | ||
- | **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. | + | **3.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. |
<code scala> | <code scala> | ||
- | def take(n: Int, l: List[Int]): List[Int] = ??? | + | def foldConditional(b: Int)(op: (Int,Int) => Int, p: Int => Boolean)(start: Int, stop: Int): Int = ??? |
- | //take(3,List(1,2,3,4,5)) = List(1,2,3) | + | |
</code> | </code> | ||
- | **3.1.3.** Write a function which //drops// the first ''n'' elements from a given list. | + | **3.3.** Implement the function ''foldRight'' which has the same behaviour as ''foldWith'', but the order in which the operation is performed is now: ''1+(2+(3+0))=6''. What is the simplest way to implement it? |
- | <code scala> | ||
- | def drop(n: Int, l: List[Int]): List[Int] = ??? | ||
- | //drop(3,List(1,2,3,4,5)) = List(4,5) | ||
- | </code> | ||
- | |||
- | **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**. | ||
+ | **3.4.** 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 \;op\;f(a_k)]. | ||
<code scala> | <code scala> | ||
- | def takeP(p: Int => Boolean)(l: List[Int]): List[Int] = ??? | + | def foldMap(op: (Int,Int) => Int, f: Int => Int)(start: Int, stop: Int): Int = ??? |
- | //takeP(_%2 == 0)(List(1,2,3,4,5,6)) = List(2,4,6) | + | |
</code> | </code> | ||
- | **3.1.5.** Write a function which uses a predicate to partition (split) a list. | + | **3.5.** Write a function which computes $math[1 + 2^2 + 3^2 + \ldots + (n-1)^2 + n^2] using ''foldMap''. |
<code scala> | <code scala> | ||
- | def part(p: Int => Boolean)(l: List[Int]): (List[Int], List[Int]) = ??? | + | def sumSquares(n: Int): Int = ??? |
- | // part(_%2 == 0)(List(1,2,3,4,5,6)) = (List(2,4,6),List(1,3,5)) | + | |
</code> | </code> | ||
- | **3.1.6.** Write a function that reverses the elements of a list. Use a ''fold'' function (determine if it should be right or left). | + | **3.6.** Write a function ''hasDivisor'' which checks if a range contains a multiple of k. Use ''foldMap'' and choose ''f'' carefully. |
<code scala> | <code scala> | ||
- | def rev(l: List[Int]): List[Int] = ??? | + | def hasDivisor(k: Int, start: Int, stop: Int): Boolean = ??? |
- | // rev(List(1,2,3,4,5,6)) = List(6,5,4,3,2,1) | + | |
</code> | </code> | ||
- | ==== 3.2. String processing ==== | + | **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: |
- | + | * 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]. | |
- | In what follows, we shall encode a String as a list of characters, using the type defined below: | + | * 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> | ||
- | type Str = List[Char] | + | def integrate(f: Double => Double)(start: Double, stop: Double): Double = ??? |
</code> | </code> | ||
- | Add this type alias to your code before solving the following exercises. | ||
- | The following is an input test. You can add more examples to it: | + | **3.8.** We define ''Line2D'' to be lines in a 2-dimensional space, and we represent them as functions. Write a function which takes a Line2D and translates it up on the Ox axis by a given offset. For instance, translateOx of 2 on y=x+1 will return y=x+3. |
<code scala> | <code scala> | ||
- | val l: List[Str] = List("matei@gmail.com", "mihai@gmail.com", "tEst@mail.com", "email@email.com", "short@ax.ro").map(x => x.toList) | + | type Line2D = Int => Int |
+ | def translateOx(offset: Int)(l: Line2D): Line2D = ??? | ||
</code> | </code> | ||
- | Use ''map'', ''foldr''/''foldl'', instead of recursive functions. | + | **3.9.** (!) Write a function which takes a Line2D and translates it up on the Oy axis by a given offset. |
- | + | ||
- | **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 remUpper(list: List[Str]): List[Str] = ??? | + | def translateOy(offset: Int)(l: Line2D): Line2D = ??? |
</code> | </code> | ||
- | **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). | + | **3.10.** Write a function which takes two lines, and checks if there exist integer coordinates within a given range for x, where the lines intersect. |
<code scala> | <code scala> | ||
- | def longer(k: Int, list: List[Str]): List[Str] = ??? | + | def intersect(l1: Line2D, l2: Line2D)(start: Int, stop: Int): Boolean = ??? |
- | </code> | + | |
- | + | ||
- | **3.2.3.** Count the number of emails longer than k characters. Use ''foldRight''. | + | |
- | + | ||
- | <code scala> | + | |
- | def howMany(k: Int)(list: List[Str]): Int = ??? | + | |
- | </code> | + | |
- | + | ||
- | **(!) 3.2.4.** Implement a function that tokenizes a String split by a given delimiter using ''foldRight''. Try to figure out what the accumulator should do. | + | |
- | <code scala> | + | |
- | def mySplit(l: Str, sep: Char): List[Str] = ??? | + | |
- | </code> | + | |
- | + | ||
- | **3.2.5.** Implement a function that return the domains without the dot (ex. ''gmail''). | + | |
- | <code scala> | + | |
- | def domains(list: List[Str]): List[Str] = ??? | + | |
- | </code> | + | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | ==== 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> | + | |
- | val l = List(1,2,3,4,5,6,7,8,9) | + | |
- | l.take(3) | + | |
- | l.drop(3) | + | |
- | l.partition(_%2 == 0) | + | |
- | </code> | + | |
- | + | ||
- | 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> | + | |
- | type Gradebook = List[(Str,Int)] //the type Gradebook now refers to a list of pairs of String and Int | + | |
- | </code> | + | |
- | Add this type alias to your code before solving the following exercises. | + | |
- | + | ||
- | **3.3.1.** Write a function which adds one point to all students that satisfy a given predicate (ex: grade >= 5), and leaves all other grades unchanged. | + | |
- | <code scala> | + | |
- | def increment(g: Gradebook, p: (Str, Int) => Boolean): Gradebook = | + | |
- | g.map(???) | + | |
- | </code> | + | |
- | + | ||
- | **3.3.2.** Find the average grade from a gradebook. You must use ''foldRight''. | + | |
- | <code scala> | + | |
- | def average(g: Gradebook): Double = ??? | + | |
- | </code> | + | |
- | + | ||
- | **3.3.3.** Write a function which takes a gradebook and returns the list of names which have passed. Use filter and map from Scala. | + | |
- | <code scala> | + | |
- | def pass(g: Gradebook): List[Str] = ??? | + | |
- | </code> | + | |
- | + | ||
- | **3.3.4.** Implement merge-sort (in ascending order) over gradebooks: | + | |
- | <code scala> | + | |
- | def mergeSort(l: Gradebook): Gradebook = { | + | |
- | def merge(u: Gradebook, v: Gradebook): Gradebook = ??? | + | |
- | ??? | + | |
- | } | + | |
</code> | </code> | ||
- | **3.3.5** Write a function which takes a gradebook and reports all passing students in **descending** order of their grade. | + | **3.11.** Write a function which takes two lines and a range of integers, and checks if l1 has larger y values than l2 over the entire range. |
<code scala> | <code scala> | ||
- | def honorsList(g: Gradebook): List[Str] = ??? | + | def larger(l1: Line2D, l2: Line2D)(start: Int, stop: Int): Boolean = ??? |
</code> | </code> |