Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
fp:lab03 [2021/03/25 14:37] pdmatei [3.1. Pattern matching revisited] |
fp:lab03 [2022/03/18 15:31] (current) pdmatei |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== 3. Recursive functions and tail-end recursion ====== | + | ====== 3. Higher-order functions ====== |
- | ===== 3.1. Pattern matching revisited ===== | + | Objectives: |
+ | * implement and use **higher-order** functions. A **higher-order** function takes other functions as parameter or returns them | ||
+ | * implement **curry** and **uncurry** functions, and how they should be properly used (review lecture). | ||
- | It is likely that in the above implementations you used ''head'' and ''tail'', or branch definitions combined with '_' to solve the exercises. Haskell supports a more elegant means of //value introspection//, called **pattern matching**. For instance, in: | + | **3.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). |
- | <code haskell> | + | <code scala> |
- | f [] = ... | + | def foldWith (op: (Int,Int) => Int)(start: Int, stop: Int): Int = { |
- | f (h:t) = ... | + | def tail_fold(crt: Int, acc: Int): Int = ??? |
- | </code> | + | ?? |
+ | } | ||
+ | </code> | ||
- | ''(h:t)'' is a **pattern** which denotes a non-empty list where ''h'' is the first element and ''t'' is the rest of the list. In fact, ''[]'' is also a pattern denoting the empty lists. Patterns are **allways** surrounded by round parentheses. They can also be composite, as in the exercise below: | + | **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. |
- | 3.1.1. Write a function which returns the number of elements from a list. Use patterns. | + | <code scala> |
+ | def foldConditional(op: (Int,Int) => Int, p: Int => Boolean)(start: Int, stop: Int): Int = ??? | ||
+ | </code> | ||
- | 3.1.2. Write a function which takes a list of integer lists, and concatenates them. (E.g. ''f [ [1,2,3],[4,5] ] = [1,2,3,4,5]''). | + | **3.3. [//should be revised//] ** Let $math[count_k(n) = k + 2k + 3k + ... x*k], with $math[ x*k \leq n] be the sum of all multiples of $math[k] within the range 1,n. Write a function ''alldivs'' which computes the sum: $math[count_1(n) + count_2(n) + ... + count_k(n)]. (Hint, use ''foldConditional''). |
- | 3.1.3 (!) Write the **same** function, this time using only the **cons** ('':'') operator as well as **patterns**. Hint: | + | <code scala> |
- | <code haskell> | + | def alldivs(n: Int): Int = ??? |
- | f [[1,2,3],[4,5]] = | + | |
- | 1:(f [[2,3],[4,5]]) = | + | |
- | 1:2:(f [[3],[4,5]]) = | + | |
- | 1:2:3:[[],[4,5]] = | + | |
- | 1:2:3:[[4,5]] = | + | |
- | 1:2:3:4:[[5]] = | + | |
- | 1:2:3:4:5:[[]] = | + | |
- | 1:2:3:4:5:[] | + | |
</code> | </code> | ||
- | 3.1.4. Write a function which removes duplicates from a list of integers. | + | **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 f(a_k)]. |
+ | <code scala> | ||
+ | def foldMap(op: (Int,Int) => Int, f: Int => Int)(start: Int, stop: Int): Int = ??? | ||
+ | </code> | ||
- | ===== 3.2. Tail-end recursive functions ===== | + | **3.5.** Write a function which computes $math[1 + 2^2 + 3^2 + \ldots + (n-1)^2 + n^2] using ''foldMap''. |
+ | <code scala> | ||
+ | def sumSquares(n: Int): Int = ??? | ||
+ | </code> | ||
- | 3.2.1 Solve exercise 3.1.3. using a tail-recursive function. | + | **3.6.** Write a function ''hasDivisor'' which checks if a range contains a multiple of k. Use ''foldMap'' and choose ''f'' carefully. |
+ | <code scala> | ||
+ | def hasDivisor(k: Int, start: Int, stop: Int): Boolean = ??? | ||
+ | </code> | ||
- | 3.2.2. Solve exercise 3.1.4. using a tail-recursive function. | + | **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]. | ||
+ | * 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. | ||
- | 3.2.3. Return all duplicates from a list, using a tail-recursive function. E.g., the duplicates from list ''[1,2,3,4,2,4,2,1]'' are ''[2,4,2,1]''. | + | Implement the function ''integrate'' which computes the integral of a function f given a range: |
- | + | <code scala> | |
- | ===== 3.3. Strings in Haskell ===== | + | def integrate(f: Double => Double)(start: Double, stop: Double): Double = ??? |
- | + | ||
- | In Haskell, strings are implemented as **lists of type** ''Char''. For instance '''a' '' is a ''Char''. Also, the string ''"abc"'' is the same as: '' 'a':'b':'c':[]''. Strings can be introspected (using pattern matching) and constructed exactly as any other list (using cons '':'', ''++'', etc.). | + | |
- | + | ||
- | 3.3.1. Write a function which takes a list of words and makes the first letter of each word uppercase. (Hint: google a respective function of chars). | + | |
- | + | ||
- | 3.3.2. Write a function which takes a list of words and makes **all** letters uppercase. | + | |
- | + | ||
- | 3.3.3. (!) Write a function which takes a text and a pattern and returns the number of occurrences of the pattern in the text. Do **not** use auxiliary functions. | + | |
- | Example: | + | |
- | <code haskell> | + | |
- | search "I eat green apples" "eat" = 1 | + | |
- | search "ababaab" "aba" = 2 | + | |
</code> | </code> | ||
- | |||
- | |||
- | |||
- | |||
- |