Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
fp:lab04 [2021/04/01 12:18] pdmatei |
fp:lab04 [2022/05/15 23:54] (current) vbadoiu old revision restored (2022/03/25 10:15) |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== 4. Higher order functions ====== | + | ===== 4. Lists in Scala ===== |
- | ===== 4.1. Map, foldl, foldr, filter, zipWith ===== | + | Objectives: |
+ | * get familiar with **pattern matching** lists, as well as common list operations from Scala and how they work | ||
+ | * get familiar with common **higher-order functions** over lists (partition, map, foldRight, foldLeft, filter) | ||
- | ==== Map ==== | + | ==== 4.1. Common list operations ==== |
- | The function ''map'' has signature: ''(a->b) -> [a] -> [b]''. It takes as parameter: | + | |
- | - a **transformer** function ''f :: a->b'' | + | |
- | - a list of objects of type ''a'' | + | |
- | It returns: | + | **4.1.1.** Write a function which returns true if a list of integers has at least k elements. Use patterns. |
- | - a list of transformed objects of type ''b'' | + | <code scala> |
+ | def atLeastk(k: Int, l: List[Int]): Boolean = | ||
+ | if (k == 0) ??? | ||
+ | else ??? | ||
+ | } | ||
+ | </code> | ||
- | 4.1.1. Write a function which takes a list of integers and adds 4 to **each element**. | + | **4.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> | ||
+ | def take(n: Int, l: List[Int]): List[Int] = ??? | ||
+ | //take(3,List(1,2,3,4,5)) = List(1,2,3) | ||
+ | </code> | ||
- | 4.1.2. Write a function which takes a list of strings and adds a whitespace at the **beginning** of each string. | + | **4.1.3.** Write a function which //drops// the first ''n'' elements from a given list. The function should not be implemented as tail-recursive. |
- | + | <code scala> | |
- | 4.1.3. Write a function which takes a value k, a list of **strings** and returns a list with the first k characters of each string. | + | def drop(n: Int, l: List[Int]): List[Int] = ??? |
- | + | //drop(3,List(1,2,3,4,5)) = List(4,5) | |
- | ==== Foldr ==== | + | </code> |
- | + | ||
- | The function ''foldr'' has type ''(a -> b -> b) -> b -> [a] -> b''. It takes as parameter: | + | |
- | * a **binary** operation ''a -> b -> b''. It takes a "list" element of type ''a'', and an **accumulator** of type ''b'' and returns an element of type ''b'' | + | |
- | * an **accumulator** (or initial value) of type ''b'' | + | |
- | * a list with elements of type ''a'' | + | |
- | + | ||
- | It returns: | + | |
- | * a **folded** (or reduced) value of type ''b''. | + | |
- | Example: | + | **4.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 haskell> | + | <code scala> |
- | foldr (*) 0 [1,2,3] = 1 * (2 * (3 * 0)) | + | def takeP(p: Int => Boolean)(l: List[Int]): List[Int] = ??? |
+ | //takeP(_%2 == 0)(List(1,2,3,4,5,6)) = List(2,4,6) | ||
</code> | </code> | ||
- | 4.1.4. Write a function using foldr which computes the product of a list of integers. | + | **4.1.5.** Write a function which uses a predicate to partition (split) a list. |
- | + | <code scala> | |
- | 4.1.5. Write a function which computes the maximum of a list of **positive** integers using foldr. | + | def part(p: Int => Boolean)(l: List[Int]): (List[Int], List[Int]) = ??? |
- | + | // part(_%2 == 0)(List(1,2,3,4,5,6)) = (List(2,4,6),List(1,3,5)) | |
- | 4.1.6. Write a function which concatenates a list of strings using foldr. | + | |
- | + | ||
- | ==== Foldl ==== | + | |
- | + | ||
- | The function ''foldr'' has type ''(b -> a -> b) -> b -> [a] -> b''. It takes as parameter: | + | |
- | * a **binary** operation ''b -> a -> b''. It takes an **accumulator** of type ''b'', a "list" element of type ''a'' and returns an element of type ''b'' | + | |
- | * an **accumulator** (or initial value) of type ''b'' | + | |
- | * a list with elements of type ''a'' | + | |
- | + | ||
- | It returns: | + | |
- | * a **folded** (or reduced) value of type ''b''. | + | |
- | + | ||
- | Unlike foldr, foldl reduces element in a different order: | + | |
- | Example: | + | |
- | <code haskell> | + | |
- | foldl (*) 0 [1,2,3] = ((0 * 1) * 2) * 3 | + | |
</code> | </code> | ||
- | 4.1.7. Write a function which uses foldl to check if an element is member of a list. | + | ==== 4.2. Gradebooks ==== |
- | + | More general implementation of ''taken'', ''dropn'' and ''part'' are already implemented in Scala and can be used as member functions of lists. Examples are shown below: | |
- | 4.1.8. Write a function which uses foldl to implement the reversal of a list. | + | <code scala> |
- | + | val l = List(1,2,3,4,5,6,7,8,9) | |
- | ==== Filter ==== | + | l.take(3) |
- | + | l.drop(3) | |
- | The function ''filter :: (a -> Bool) -> [a] -> [a]'' takes as parameter: | + | l.partition(_%2 == 0) |
- | * a **predicate** (or condition) of type ''(a -> Bool)'' | + | |
- | * a list of elements of type ''a'' | + | |
- | + | ||
- | It returns: | + | |
- | * a list of filtered elements using the predicate. | + | |
- | + | ||
- | 4.1.9. Write a function which removes all strings which are equal to the empty string. | + | |
- | + | ||
- | 4.1.11. Write a function which takes a list of strings and returns only those which start with a capital. | + | |
- | + | ||
- | ==== zipWith ==== | + | |
- | + | ||
- | 4.1.12. Use ''ghci> :t zipWith'' to inspect the type of ''zipWith''. Think about a possible example to run this function. What does it do? | + | |
- | + | ||
- | + | ||
- | ===== 4.2. Processing using higher-order functions ===== | + | |
- | + | ||
- | The following is an input test. You can add more examples to it: | + | |
- | <code haskell> | + | |
- | l = ["matei@gmail.com", "mihai@gmail.com", "tEst@mail.com", "email@email.com", "short@ax.ro"] | + | |
</code> | </code> | ||
- | Use ''map'', ''foldr''/''foldl'', instead of recursive functions. Wherever possible, use functional composition and closures. | + | 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(("G",3), ("F", 10), ("M",6), ("P",4)) | ||
+ | </code> | ||
- | 4.2.1. Remove uppercases from emails. (Do **not** use recursion). To be able to use character functions from the library, add ''import Data.Char'' at the beginning of the program. Use the Internet to find the appropriate character function. | + | To make the type signatures more legible, we can introduce type aliases in Scala: |
- | + | <code scala> | |
- | <code haskell> | + | type Gradebook = List[(String,Int)] //the type Gradebook now refers to a list of pairs of String and Int |
- | -- write this function as a closure | + | |
- | rem_upper = | + | |
</code> | </code> | ||
+ | Add this type alias to your code before solving the following exercises. | ||
- | 4.2.2. Write a function which removes emails longer than a given size. Write the function as a **functional closure**. Use anonymous functions in your implementation, then think about how you can replace them by a functional composition of more basic functions. **Hint:** Write your code in steps. Start with the basic idea, then think about how you can write it better and cleaner. | + | **4.2.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 haskell> | + | def increment(g: Gradebook): Gradebook = |
- | longer :: Int -> [String] -> [String] | + | g.map(???) |
- | longer x = | + | |
</code> | </code> | ||
- | 4.2.3. Count the number of emails longer than 12 characters. Use a fold, anonymous functions and functional composition. | + | **4.2.2.** Find the average grade from a gradebook. You must use ''foldRight''. |
- | <code haskell> | + | <code scala> |
- | howmany = | + | def average(g: Gradebook): Double = ??? |
</code> | </code> | ||
- | 4.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. | + | **4.2.3.** Write a function which takes a gradebook and returns the percentage of failed vs. passed students, as a pair (x,y). |
- | <code haskell> | + | <code scala> |
- | names_emails :: [String] -> [[String]] | + | def percentage(g: Gradebook): (Double,Double) = ??? |
- | names_emails = | + | |
</code> | </code> | ||
- | 4.2.5. Identify the list of the employed domain names (e.g. ''gmail.com''). Remove duplicates. Use no recursion and no additional prelude function apart from ''head'' and ''tail''. **Hint** think about the sequence of basic operations you want to perform and assemble them using functional composition. | + | **4.2.4.** Write a function which takes a gradebook and returns the list of names which have passed. Use filter and map from Scala. |
- | <code haskell> | + | <code scala> |
- | domains :: [String] -> [String] | + | def pass(g: Gradebook): List[String] = ??? |
- | domains = | + | |
</code> | </code> | ||
- | (!) 4.2.6. In some previous exercise you have, most likely, implemented a split function using ''foldr''. Implement one with ''foldl''. **Hint:** use an example together with the ''foldl'' implementation to figure out what the accumulator should do. | + | **4.2.5.** Implement merge-sort (in ascending order) over gradebooks: |
- | + | <code scala> | |
- | <code haskell> | + | def mergeSort(l: Gradebook): Gradebook = { |
- | splitl :: String -> [String] | + | def merge(u: Gradebook, v: Gradebook): Gradebook = ??? |
- | splitl = | + | ??? |
+ | } | ||
</code> | </code> | ||
- | 4.2.7. Write a function which extracts the domains from emails, without the dot part. (e.g. ''gmail''). Generalise the previous function ''splitl'' to ''splitBy:: Char -> String -> [String]'', and use it each time necessary, in your implementation. **Hint**: Wherever you want to mix pattern matching with guards, start with the patterns first. | + | **4.2.6** Write a function which takes a gradebook and reports all passing students in **descending** order of their grade. |
- | + | <code scala> | |
- | <code haskell> | + | def honorsList(g: Gradebook): List[String] = ??? |
- | domain :: [String] -> [String] | + | |
- | domain = | + | |
</code> | </code> | ||
- |