Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
pp:l03 [2020/02/10 12:51] dmihai [foldr & foldl] |
pp:l03 [2022/03/20 15:13] (current) bogdan.deac |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Aplicații cu funcții de ordin superior ====== | + | ====== 3. Higher-order functions ====== |
- | Scopul laboratorului este de a exersa lucrul cu funcții de ordin superior. | + | ==== Function application and composition as higher-order functions ==== |
+ | One key idea from functional programming is that functions are **first-class** (or **first-order**) values, just like integers, strings, etc. . They can be passed as function **arguments** and also be returned by function application. | ||
- | ===== Recapitulare ===== | + | Functions which //take other functions as parameter// are called **higher-order**. |
- | O **funcție de ordin superior** este //o funcție care primește ca argument sau returnează o funcție//. | + | ==== Lambdas ==== |
+ | Functions can be passed as arguments just like any other value value. Also, functions can be returned as parameter. In order to do so, it is convenient to define functions without naming them. This is done using **lambda**'s. For a more detailed discussion regarding lambdas, see the lecture. The following definitions are equivalent: | ||
<code haskell> | <code haskell> | ||
- | applyTwice f x = f (f x) | + | f x y = x + y |
- | adder x = (+x) | + | f x = \y -> x + y |
+ | f = \x -> \y -> x + y | ||
+ | f = \x y -> x + y | ||
</code> | </code> | ||
- | ==== foldr & foldl ==== | + | ===== 3.1. String processing ===== |
- | Două funcții de ordin superior foarte importante sunt foldurile pe liste: ''foldl'' și ''foldr''. Aceasta combină, în mod recursiv, elementele unei liste cu o valoare default, pentru a obține un rezultat. Diferența principală între ele constă în sensul de parcurgere: ''foldl'' ("fold left") începe din stânga listei, iar ''foldr'' ("fold right") începe din dreapta listei. | + | |
- | <code> | + | The following is an input test. You can add more examples to it: |
- | Prelude> foldl (^) 1 [2, 2, 2] -- (((1 ^ 2) ^ 2) ^ 2) | + | <code haskell> |
- | 1 | + | l = ["matei@gmail.com", "mihai@gmail.com", "tEst@mail.com", "email@email.com", "short@ax.ro"] |
- | Prelude> foldr (^) 1 [2, 2, 2] -- (2 ^ (2 ^ (2 ^ 1))) | + | |
- | 16 | + | |
- | Prelude> | + | |
</code> | </code> | ||
- | Dacă operația aleasă este [[https://en.wikipedia.org/wiki/Commutative_property|comutativă]] și [[https://en.wikipedia.org/wiki/Associative_property|asociativă]], rezultatele vor fi aceleași, doar ordinea operațiilor fiind diferită. | + | Use ''map'', ''foldr''/''foldl'', instead of recursive functions. Wherever possible, use functional composition and closures. |
- | Observați că și tipul funcției primită ca argument diferă; ''foldl'' trimite elementele listei ca al doilea parametru, iar ''foldr'' ca prim parametru. | + | 3.1.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. |
<code haskell> | <code haskell> | ||
- | foldr (:) [] l -- întoarce o lista identică cu l | + | -- write this function as a closure |
- | foldl (flip (:)) [] l -- întoarce inversul listei l | + | rem_upper = |
</code> | </code> | ||
- | Tipurile acestor funcții sunt, deci: | + | 3.1.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. |
- | <code> | + | |
- | foldl :: (b -> a -> b) -> b -> [a] -> b | + | <code haskell> |
- | foldr :: (a -> b -> b) -> b -> [a] -> b | + | longer :: Int -> [String] -> [String] |
+ | longer x = | ||
</code> | </code> | ||
- | Contemplați aceste tipuri până vă este clar de ce sunt așa. | + | 3.1.3. Count the number of emails longer than 12 characters. Use a fold, anonymous functions and functional composition. |
+ | <code haskell> | ||
+ | howmany = | ||
+ | </code> | ||
- | <note> | + | 3.1.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. |
- | În Haskellul modern, tipul acestor funcții este și mai generic, funcționând pe mai multe tipuri de structuri de date considerate "foldable", nu doar pe liste: | + | <code haskell> |
+ | names_emails :: [String] -> [[String]] | ||
+ | names_emails = | ||
+ | </code> | ||
- | <code> | + | 3.1.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. |
- | Prelude> :t foldl | + | <code haskell> |
- | foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b | + | domains :: [String] -> [String] |
- | Prelude> :t foldr | + | domains = |
- | foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b | + | |
</code> | </code> | ||
- | (Mai multe detalii despre notații ca ''%%=%%>'' și ''t a'' la cursurile/laboratoarele viitoare). | + | (!) 3.1.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. |
- | </note> | + | |
+ | <code haskell> | ||
+ | splitl :: String -> [String] | ||
+ | splitl = | ||
+ | </code> | ||
- | === Tail-recursion și evaluare leneșă === | + | 3.1.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. |
- | O privire la implementarea celor două folduri: | + | <code haskell> |
+ | domain :: [String] -> [String] | ||
+ | domain = | ||
+ | </code> | ||
+ | ===== 3.2. A predicate-based implementation for sets ===== | ||
+ | 3.2.1. Consider **sets** represented as characteristic functions with signature ''s :: Integer -> Bool'', where ''s x'' is true if ''x'' a member in the set. Examples: | ||
<code haskell> | <code haskell> | ||
- | foldl _ e [] = e | + | s1 1 = True |
- | foldl f e (x:xs) = foldl f (f e x) xs | + | s1 2 = True |
+ | s1 _ = False | ||
- | foldr _ e [] = e | + | s2 x = mod x 2 == 0 |
- | foldr f e (x:xs) = f x (foldr f e xs) | + | |
+ | s3 _ = False | ||
+ | |||
+ | </code> | ||
+ | Above, ''s1'' is the set $math[\{1,2\}], ''s2'' is the set of even integers and ''s3'' is the empty-set. Write a function which tests if an element is a member of a set: | ||
+ | <code haskell> | ||
+ | mem :: (Integer -> Bool) -> Integer -> Bool | ||
+ | mem = ... | ||
</code> | </code> | ||
- | dezvăluie o altă diferență: ''foldl'' este tail-recursive, ''foldr'' nu. Concluzia naivă este că ''foldl'' este mai eficient, deci mai "bun". Situația însă este mai complicată din cauza evaluării leneșe din Haskell. ''foldr'' permite lucrul cu liste infinite, iar ''foldl'' de fapt nu oferă niciun avantaj cu privire la spațiul de stivă utilizat; mai multe detalii la cursuri/laboratoare despre evaluare leneșă. | + | 3.2.2. Define the set $math[\{2^n \mid n\in\mathbb{N}\}]. |
- | === Vizualizare === | + | 3.2.3. Define the set of natural numbers. |
- | Următoarea este o vizualizare a rezultatului următoarelor două aplicări (pentru oricare ''f'' și ''z''): | + | 3.2.4. Implement the intersection of two sets. Use lambdas. |
+ | <code haskell> | ||
+ | intersection :: (Integer -> Bool) -> (Integer -> Bool) -> (Integer -> Bool) | ||
+ | </code> | ||
- | <code> | + | 3.2.5. Write intersection in another way, (without using lambdas). |
- | foldr f z [1, 2, 3, 4, 5] | + | <code haskell> |
- | foldl f z [1, 2, 3, 4, 5] | + | intersection' :: (Integer -> Bool) -> (Integer -> Bool) -> Integer -> Bool |
</code> | </code> | ||
- | {{ :pp:fold-visualization.png?800 |}} | + | 3.2.6. Write a function which takes a list of integers, and returns the set which contains them. |
+ | <code haskell> | ||
+ | toSet :: [Integer] -> (Integer -> Bool) | ||
+ | </code> | ||
- | Alte funcții de ordin superior des întâlnite: ''map'', ''filter'', ''zipWith'', ''flip''. | + | 3.2.7. Implement a function which takes a list of sets and computes their intersection. |
+ | <code haskell> | ||
+ | capList :: [Integer -> Bool] -> Integer -> Bool | ||
+ | </code> | ||
- | ===== Exerciții ===== | + | ===== 3.3. Brain twisters ===== |
+ | 3.3.1. Implement ''map'' using ''foldl'' and ''foldr'' | ||
+ | |||
+ | <code haskell> | ||
+ | mapr :: (a -> b) -> [a] -> [b] | ||
+ | mapl :: (a -> b) -> [a] -> [b] | ||
+ | </code> | ||
+ | |||
+ | 3.3.2. Implement ''filter'' using ''foldl'' and ''foldr'' | ||
+ | |||
+ | <code haskell> | ||
+ | filterl :: (a -> Bool) -> [a] -> [a] | ||
+ | filterr :: (a -> Bool) -> [a] -> [a] | ||
+ | </code> | ||
+ | |||
+ | 3.3.3. Implement ''foldl'' using ''foldr'' | ||
+ | <code haskell> | ||
+ | myfoldl :: (a -> b -> a) -> a -> [b] -> a | ||
+ | </code> | ||
+ | 3.3.4. Implement ''bubbleSort''. It must use at least one fold | ||
+ | <code haskell> | ||
+ | bubbleSort :: [Integer] -> [Integer] | ||
+ | </code> | ||
- | - Fie două matrici reprezentate ca liste de liste. În rezolvarea exercițiilor de mai jos, puteți folosi doar funcții de ordin superior (împreună cu ''take'' și ''drop''). Implementați funcții care să returneze: | ||
- | - linia ''i'' dintr-o matrice | ||
- | - elementul ''(i, j)'' dintr-o matrice | ||
- | - suma a două matrici | ||
- | - transpusa unei matrici | ||
- | - produsul a două matrici | ||
- | - Fie o imagine reprezentată ca o matrice de caractere (numiți, în continuare, "pixeli"). Considerăm că avem două tipuri de pixeli: ''%%'%%*%%'%%'' și ''%%'%% %%'%%''. Implementați următoarele funcții: | ||
- | - flip orizontal | ||
- | - flip vertical | ||
- | - rotație de 90 de grade în sens trigonometric | ||
- | - rotație de 90 de grade în sens invers trigonometric | ||
- | - negativul (''%%'%%*%%'%%'' devine ''%%'%% %%'%%'', iar ''%%'%% %%'%%'' devine ''%%'%%*%%'%%'') | ||
- | - scalarea unei imagini cu ''x'' unități | ||
- | - alipirea a două imagini (cu aceeași înălțime) pe orizontală | ||
- | - alipirea a două imagini (cu aceeași lungime) pe verticală | ||
- | - crop vertical de la linia ''x'' la linia ''y'' | ||
- | - crop orizontal de la coloana ''x'' la coloana ''y'' | ||
- | {{:pp:lab3_-_schelet.zip|Lab 3 - schelet}}\\ | ||
- | În afară de asserturile din schelet, aveți la dispoziție funcții și variabile ajutătoare în fișierul ''Helper.hs''. Aveți matricile ''m1'' și ''m2'', precum și rezultatul sumei și al produsului acestora, plus o imagine ''img''. | ||
- | Pentru afișarea lizibilă a unei matrici, aveți funcția ''displayMat'', iar pentru imagine ''displayImg''; acestea ar trebui să vă ajute cu testarea. | ||
- | <code> | ||
- | *Lab3> displayMat m1 | ||
- | 1 2 3 | ||
- | 4 5 6 | ||
- | 7 8 9 | ||
- | *Lab3> displayImg img | ||
- | ***** ** ***** ** | ||
- | ****** **** ****** **** | ||
- | ** * * *** ** * * *** | ||
- | * * * *** * * * *** | ||
- | * * ** * * ** | ||
- | ** ** ** ** ** ** | ||
- | ** ** ** ** ** ** | ||
- | **** ** * **** ** * | ||
- | * *** ** * * *** ** * | ||
- | ** ******* ** ******* | ||
- | ** ****** ** ****** | ||
- | ** ** ** ** | ||
- | ** ** ** ** | ||
- | ** ** ** ** | ||
- | ** ** ** ** ** ** | ||
- | *** * * *** * * | ||
- | *** * *** * | ||
- | ****** ****** | ||
- | *** *** | ||
- | *Lab3> | ||
- | </code> | ||
- | ===== Recommended Reading ====== | ||
- | * [[http://learnyouahaskell.com/higher-order-functions|Learn you a Haskell for Great Good - Chapter 6: Higher Order Functions]] | ||
- | * [[https://wiki.haskell.org/Higher_order_function|Haskell Wiki - Higher Order Functions]] | ||
- | * [[https://wiki.haskell.org/Closure|Haskell Wiki - Closures]] |