Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
pp:l02 [2020/03/01 20:20]
lfa [Exerciții]
pp:l02 [2021/03/16 19:24] (current)
roxana_elena.stiuca [2.3. Strings in Haskell]
Line 1: Line 1:
-====== ​Funcții ​======+====== ​2. Pattern matching and basic types in Haskell ​======
  
-Scopul laboratorului:​ +===== 2.1. Pattern matching =====
-  * Semnificația termenului //​aplicație//​ +
-  * Definirea funcțiilor anonime în Haskell +
-  * Curry vs uncurry +
-  * Închideri funcționale+
  
 +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:
  
-===== Funcții de ordin superior =====+<code haskell>​ 
 +f [] ... 
 +f (h:t) ... 
 +</​code> ​
  
-Funcțiile de ordin superior sunt funcții care lucrează cu alte funcțiile primesc ca parametrii sau le returnează.+''​(h:t)''​ is a **pattern** which denotes a non-empty list where ''​h''​ is the first element and ''​t''​ is the rest of the listIn 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:
  
-Pentru ​înțelege importanțlor, vom da următorul exemplu: ne propunem să scriem două funcții care primesc o listă și returnează,​ într-o nouă listă +2.1.1. Write function which returns the number of elements from a list. Use patterns.
-  * toate elementele pare +
-  * toate elementele mai mari decât 10+
  
-<code haskell>​ +2.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]''​).
-evenElements ​[] = [] +
-evenElements (x:xs) = if even x +
-                          then x : evenElements xs +
-                          else evenElements xs +
-                       +
-greaterThan10 ​[] = [] +
-greaterThan10 (x:xs= if x > 10 +
-                           then x : greaterThan10 xs +
-                           else greaterThan10 xs +
-</​code>​+
  
-<​note>​ +2.1.3 (!) Write the **same** function, this time using only the **cons** (''​:''​) operator as well as **patterns**. ​
-În loc de ''​if''​, puteți folosi următoarea sintaxă: +
-<code haskell>​ +
-myFunction x +
-        | x < 10    = "One digit"​ +
-        | x < 100   = "Two digits"​ +
-        | x < 1000  = "Three digits"​ +
-        | otherwise = "More than four digits"​ +
-</​code>​ +
-</​note>​+
  
-Testăm funcțiile scrise: +2.1.4. Write a function which removes duplicates from a list of integers.
-<​code>​ +
-*Main> evenElements [1..20] +
-[2,4,​6,​8,​10,​12,​14,​16,​18,​20] +
-*Main> greaterThan10 [1..20] +
-[10,​11,​12,​13,​14,​15,​16,​17,​18,​19,​20] +
-</​code>​+
  
-Observăm că funcțiile definite mai sus sunt foarte asemănătoareDe fapt, doar condiția verificată în ''​if''​ diferăScriem, deci, o funcție generală care primește o funcție pentru testarea elementelor:​+===== 2.2Types in Haskell =====
  
 +2.2.1. What are the types of ''​x,​y,​z,​w''​ and that of ''​l''​ in the implementation below?
 <code haskell> <code haskell>
--- In primul pattern, nu folosim functia de testare, deci nu ne intereseaza ca aceasta +(x:y:z:w:l) = w 
--- sa fie legata la un nume, lucru marcat prin "​_"​ +f _ = 0
-myFilter _ [] = [] +
-myFilter test (x:xs) = if test x +
-                           then x : myFilter test xs +
-                           else myFilter test xs+
 </​code>​ </​code>​
  
-Acum putem rescrie funcțiile noastre, într-un mod mai elegant, utilizând funcția de filtrare: +How about:
 <code haskell> <code haskell>
-evenElements = myFilter even +(x:y:z:w:l= w
- +
-greaterThan10 = myFilter ​(> 10)+
 </​code>​ </​code>​
- +What is the difference? 
- +     
-===== Currying vsuncurrying =====   +2.2.2What is the type of the function
- +<​code ​haskell
-Currying (numit după [[https://​en.wikipedia.org/​wiki/​Haskell_Curry|tizul Haskell-ului]]) este procesul prin care, dintr-o funcție care ia mai multe argumente, se obține o secvență de funcții care iau un singur argument. +f x y = (x,y)
- +
-De exemplu, dintr-o funcție de două argumente ''​f ​X × Y → Z''​ se obține o funcție\\ +
-''​curry(f) : X → (Y → Z)''​. Noua funcție ''​curry(f)''​ primește un argument de tipul ''​X''​ și întoarce o funcție de tipul ''​Y → Z''​ (adică o funcție care primește un argument de tipul ''​Y''​ și întoarce un rezultat de tip ''​Z''​). Considerând operatorul ''​→''​ asociativ la dreapta, putem omite parantezele,​ i.e. ''​curry(f) : X → Y → Z''​. +
- +
-Operația inversă se numește "​uncurry":​ ''​f : X → Y → Z,   ​uncurry(f):​ X × Y → Z''​. +
- +
-Întorcându-ne la funcția de filtrare definită mai devreme, care este tipul ei? +
- +
-<​code>​ +
-*Main> :t myFilter +
-myFilter :: (a -> Bool-> [a] -> [a]+
 </​code>​ </​code>​
  
-Și în [[pp:​l01|laboratorul trecut]]am observat că nu există o separare între domeniu și codomeniude genul\\ +In the body of a function definition''​,'' ​is a **base constructor** for pairsThe following are pairs: ''​(1,2), ("​Matei"​,20), ([1,2],3)''​. Note that there is no restriction on the type of the first and second ​values ​of a pair
-''​(-%%>%% Bool) x [a] -%%>%% [a]''​. +
- +
-<note important>​ +
-Acest lucru se datorează faptului că, în Haskell, ​**toate funcțiile iau un singur argument**. Alternativ, putem spune despre ele că sunt //​curried//​. +
- +
-Tipul funcției noastre trebuie interpretat astfel:\\ +
-primește ca argument o funcție de tipul ''​a -%%>%% Bool''​ (ia un argument și întoarce o booleană) și întoarce o funcție de tipul ''​[a] -%%>%% [a]''​ (ia o listă și întoarce o listă de același tip). +
- +
-De aceeaîn exemplul de mai sus am putut definit ''​evenElements''​ (o funcție care ia o listă și returnează o listăca fiind ''​myFilter even''​ care are exact acest tipi.e. ''​[a] -%%>%% [a]''​. +
-</​note>​ +
- +
-<​note>​ +
-Haskell pune la dispoziție funcțiile ''​curry''​ și ''​uncurry''​.  +
- +
-<​code>​ +
-*Main> :t curry +
-curry :: ((ab-> c) -> a -> b -> c +
-*Main> :t uncurry +
-uncurry :: (a -> b -> c) -> (ab) -> c +
-</​code>​ +
- +
-Funcțiile primite, respectiv returnate de ''​curry''​ și ''​uncurry''​ iau tot un singur argument, numai că acesta este un tuplu. +
- +
-<​code>​ +
-*Main> let evenElements = (uncurry myFilter) even +
- +
-<​interactive>:​12:​39:​ +
-    Couldn'​t match expected type '(a0 -> Bool[a0])' +
-                with actual type 'a1 -> Bool'​ +
-    In the second ​argument ​of '​uncurry',​ namely '​even'​ +
-    In the expression: (uncurry myFilter) even +
-    In an equation for '​evenElements':​ +
-        evenElements = (uncurry myFilter) even +
-*Main> let evenElements l = (uncurry myFilter) (even, l) +
-*Main> +
-</​code>​ +
- +
-</​note>​ +
- +
-Funcțiile le putem apela și cu un număr mai mic de argumente. În acest caz, funcția apelată ​fost parțial aplicată și va rezulta o nouă funcție cu un număr mai mic de parametrii. +
- +
-<​code>​ +
-f a b = a - b +
-</​code>​ +
-Funcția ''​f''​ primește 2 parametrii și întoarce diferența dintre ei. +
-Dacă apelăm ''​f''​ cu un singur argument se întoarce o altă funcție care se așteaptă să primească un singur argument. +
-<​code>​ +
-g = f 3 -- se crează o altă funcție care primește un singur parametru (g b = 3 - b) +
-g 4 -- se apeleaza funcția g +
--1 -- rezultatul 3 - 4 +
-</​code>​ +
- +
-===== Funcții anonime ===== +
- +
-Ne propunem să scriem o funcție care primește o listă și întoarce toate elementele ei divizibile cu 5. Având deja o funcție de filtrare, putem scrie:+
  
 +2.2.3. What is the type of the function:
 <code haskell> <code haskell>
-testDiv5 ​x = mod 5 == 0 +f '​a'​ _ = [] 
-multiplesOf5 = myFilter testDiv5+= x:y
 </​code>​ </​code>​
  
-Această abordare funcționeazăînsă am poluat spațiul de nume cu o funcție pe care nu o folosim decât o singură dată - ''​testDiv5''​.+In Haskellthe type ''​String'' ​is equivalent to ''​[Char]''​ (hence strings are lists of chars). Strings can be introspected using patterns just like any other list.
  
-Funcțiile anonime (cunoscute și ca expresii lambda) sunt funcții fără nume, folosite des în lucrul cu funcții de ordin superior. +2.2.4Let f be the function below:
- +
-În Haskell, se folosește sintaxa: ''​\x -%%>%% x + 5''​Funcția definită ia un parametru și returnează suma dintre acesta și 5Caracterul ''​\''​ este folosit pentru că seamănă cu λ (lambda). +
- +
-Rescriind funcția noastră, obținem:+
 <code haskell> <code haskell>
-multiplesOf5 = myFilter ​(\x -> mod x 5 == 0)+    f "​321CB"​ [("​321CB",​ ["​Matei",​ "​Andrei",​ "​Mihai"​]), ("​322CB",​["​George",​ "​Matei"​])] = ["​Matei",​ "​Mihai"​]
 </​code>​ </​code>​
  
-Următoarele expresii sunt echivalente:​+What is the signature of ''​f''?​
  
-<code haskell> 
-f x y = x + y 
-f x = \y -> x + y 
-f = \x y -> x + y 
-</​code>​ 
  
-<​note>​ +===== 2.3. Strings in Haskell =====
-Nu există vreo diferență între ''​\x y -%%>%% x + y''​ și ''​\x -%%>%% \y -%%>%% x + y''​. +
-</​note>​+
  
-Exemple de alte limbaje unde se pot folosi funcții anonime: +2.3.1. Write a function ​which takes list of words and makes the first letter of each word uppercase.
-  * Python +
-     <​code python>​print((lambda x, y: x + y)(3, 4))</​code>​ +
-  * C++ - începând cu C++11 +
-     <​code c++>​std::​cout << [](int ​a, int b){return a + b;}(3, 4) << std::​endl;</​code>​ +
-  * JavaScript +
-     <​code javascript>​print(function(a,​b){return a+b}(3, 4))</​code>​ +
-===== Închideri funcționale ===== +
-Închiderile funcționale (**closures**) se folosesc de variabile libere în definiția lor.  Cu alte cuvinte, sunt funcții care, pe lângă definiție, conțin și un **environment** de care se folosesc; acest environment conține variabilele libere despre care vorbeam. ​ Denumirea provine din faptul că environment-ul nu este menționat explicit, el este determinat implicit; spunem că funcția **se închide** peste variabilele (//​libere//​) **a**, **b**, etc +
  
-<code haskell>​ +2.3.2. Write function which takes list of words and makes **all** letters uppercase.
-flip f = \-> \b -> f b a +
-      -- ^^^^^^^^^^^^+^^^^  +
-      --     f este o variabilă liberă în contextul funcției anonime de după egal +
-</​code>​+
  
-Ce ne returnează funcția flip, atunci când îi dăm un singur argument? ​ O //​închidere funcțională//​ **peste f**.  Prin urmare, funcția **mod_flipped** de mai jos este o închidere funcțională care atunci când primește ​parametrii va folosi funcția **mod** ​(stocată - într-un mod neobservabil -- în **contextul** săuși va returna restul împărțirii celui de-al doilea la primul+2.3.3. (!Write a function which takes a text and a pattern and returns the number of occurrences of the pattern in the text.  
- +Example: 
-<code haskell>​ +<code haskell>  
-mod_flipped ​flip mod+search "I eat green apples"​ "​eat" ​
 +search "​ababaab"​ "​aba"​ = 2
 </​code>​ </​code>​
  
-Alte exemple de închideri funcționale:​+2.3.4. (!) What does ''​f'',​ defined in exercise 2.2.4., do (note that ''​Matei''​ and ''​Mihai''​ both start with letter '​M'​)?​ Write an implementation for ''​f''​.
  
 +2.3.5. Write the signature and implement the following function:
 <code haskell> <code haskell>
-mod3 x = mod x 3 -- închidere funcțională peste funcția mod din Prelude și constanta 3 +   f ["​Matei",​ "​Mihai"​] ["​Popovici","​Dumitru"​] ​[("​Matei","​Popovici"​)("Mihai","​Dumitru"​)]
-plus5 x x + 5  -- închidere funcțională peste funcția ​(+și constanta 5 +
-(+5)             -- aceeași ca mai sus; +
-                 ​-- ​    (+) este o funcție care primește 2 argumente și se evaluează la suma lor +
-                 ​-- ​    (+5) este închiderea funcțională rezultată în urma "hardcodării" ​unuia dintre argumente+
 </​code>​ </​code>​
  
-===== Hoogle ===== +2.3.6Implement the following function:
- +
-Hoogle este un motor de căutare pentru funcții Haskell, o unealtă foarte utilă pentru dezvoltatori,​ mai ales la început. Vă recomandăm să citiți [[pp:​hoogle|această pagină]]. +
- +
-===== Exerciții ===== +
- +
-1. Definiti operatorul ''​$''​. +
- +
-2. Care este tipul operatorului de compunere al functiilor? Definiti-l printr-o functie ''​myComp''​. +
- +
-3Consideram urmatoarele functii lambda care definesc ''​f x y = x + y''​:+
 <code haskell> <code haskell>
-\y -> x + y +   ["​Matei",​ "​Mihai"​] ["​Popovici","​Dumitru"​] ​["​MPopovici",​ "​MDumitru"​]
-f = \x -> \y -> x + y +
-f = \x y -> x + y+
 </​code>​ </​code>​
-  * Care este tipul lui f? 
-  * Care este tipul lui f 5? 
  
-4Putem defini un set ca fiind o functie de tipul ''​s :: Integer -> Bool'',​ s x returneaza true daca x se afla in setul s. +2.3.7Write a function which takes a list of pairs student-name and graderemoves those with grades less than 5splits the name in substrings, and adds one bonus point to people with three namesExample:
-  * Scrieti functia s +
-  * Definiti setul ''​f = {1,2,3}''​ (tot ca o functie) +
-  * Scrieti o functie n pentru multimea numerelor naturale +
-  * Scrieti, in 2 moduri, intersectia a 2 seturi +
-  * (hard) Scrieti o functie care primeste o lista de seturi si calculeaza intersectia acestora +
- +
-5Definiti o functie ''​myFilter''​ care primeste o functie ''​g :: Integer ​-> Bool'',​ o lista de numere intregi, si returneaza o lista cu numerele pentru care g returneaza True. +
-    +
-6. Folositi functia myFilter definita anterior, scrieti o functie care pastreaza numerele pozitive dintr-o lista de numere intregi +
- +
-7. Implementati functia ''​map''​. Folosind functia mapscrieti o functie care primeste o lista de Boolsi returneaza o lista cu 1 in loc de True si 0 in loc de False. +
- +
-8. Folosind map si/sau filter, scrieti o functie care sa implementeze comportamentul:+
 <code haskell> <code haskell>
-"​321CB" ​[("321CB",​ ["Matei", ​"​Andrei"​, "​Mihai"​]), ("322CB",​["​George, Matei"​])] = ["Matei", "Mihai"]+    ​f [("Dan Matei Popovici",9),("​Mihai"​,4),("Andrei Alex",6)] = 
 +    [(["Dan""Matei", "​Popovici"],10),(["Andrei", "Alex"],6)]
 </​code>​ </​code>​
-  ​ 
-9. Folosind map si/sau filter, scrieti o functie care sa implementeze comportamentul:​ 
-<code haskell> 
-f [("Dan Matei Popovici",​9),​("​Mihai",​4),​("​Andrei Alex",​6)] = [(["​Dan",​ "​Matei",​ "​Popovici"​],​10),​(["​Andrei,​Alex"​],​6)] 
-</​code>​ 
- 
-10. Folosind map, scrieti o functie care primeste o lista de liste si returneaza lista obtinuta prin concatenarea lor intr-o singura lista. 
- 
-11. Rescrieti functia de la (10) folosind recursivitatea pe coada. Care sunt diferentele?​ 
- 
-12. Implementati ''​foldr''​ si ''​foldl''​. 
- 
-13. Implementati ''​concatenarea''​ folosind fold. 
- 
-14. Implementati functia ''​reverse''​ folosind fold. 
  
-15. (hard) Folosind foldr sau foldl, scrieti o functie care primeste o lista de seturi si calculeaza intersectia acestora 
  
  
-===== Recommended reading ===== 
  
-  * [[http://​learnyouahaskell.com/​syntax-in-functions|Learn you a Haskell for Great Good - Chapter 4: Syntax in Functions]] 
-  * [[http://​learnyouahaskell.com/​recursion|Learn you a Haskell for Great Good - Chapter 5: Recursion]] 
-  * [[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]] 
-  * [[https://​wiki.haskell.org/​Combinator|Haskell Wiki - Combinator]]