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/02/10 12:41]
dmihai
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 (și de ce nu contează în Haskell) +
-  * Î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 **base constructor** for pairs. The 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[a] -%%>%% [a]''​.+
  
-<note important>​ +2.2.3. What is the type of the function
-Acest lucru se datorează faptului că, în Haskell, **toate funcțiile iau un singur argument**Alternativ, putem spune despre ele că sunt //curried//. +<code haskell> 
- +'​a' ​_ = [] 
-Tipul funcției noastre trebuie interpretat astfel:\\ +f x y = x:y
-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 tip, i.e. ''[a-%%>%% [a]''​. +
-</​note>​ +
- +
-<​note>​ +
-Haskell pune la dispoziție funcțiile ''​curry''​ și ''​uncurry''​.  +
- +
-<​code>​ +
-*Main> ​:t curry +
-curry :: ((a, b) -> c) -> a -> b -> c +
-*Main> :t uncurry +
-uncurry :: (a -> b -> c) -> (a, b) -> c+
 </​code>​ </​code>​
  
-Funcțiile primiterespectiv returnate de ''​curry'' ​și ''​uncurry''​ iau tot un singur argument, numai că acesta este un tuplu. +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.
- +
-<​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ă a 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 5Având deja o funcție de filtrare, putem scrie:+
  
 +2.2.4. Let f be the function below:
 <code haskell> <code haskell>
-testDiv5 x mod x 5 == 0 +    f "​321CB"​ [("​321CB",​ ["​Matei",​ "​Andrei",​ "​Mihai"​]),​ ("​322CB",​["​George",​ "​Matei"​])] ​["​Matei",​ "​Mihai"​]
-multiplesOf5 = myFilter testDiv5+
 </​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''​.+What is the signature of ''​f''​?
  
-Funcțiile anonime (cunoscute și ca expresii lambda) sunt funcții fără nume, folosite des în lucrul cu funcții de ordin superior. 
  
-În Haskell, se folosește sintaxa: ''​\x -%%>%% x + 5''​Funcția definită ia un parametru și returnează suma dintre acesta și 5. Caracterul ''​\''​ este folosit pentru că seamănă cu λ (lambda).+===== 2.3Strings in Haskell =====
  
-Rescriind funcția noastră, obținem: +2.3.1. Write a function which takes a list of words and makes the first letter of each word uppercase.
-<code haskell>​ +
-multiplesOf5 = myFilter (\x -> mod x 5 == 0) +
-</​code>​+
  
-Următoarele expresii sunt echivalente:​+2.3.2. Write a function which takes a list of words and makes **all** letters uppercase.
  
-<code haskell>​ +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.  
-f x y x + y +Example: 
-f x \y -> x + y +<code haskell>  
-f = \x y -> x + y+search "I eat green apples"​ "​eat" ​1 
 +search "​ababaab"​ "​aba" ​2
 </​code>​ </​code>​
  
-<​note>​ +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''​.
-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: +
-  * Python +
-     <​code python>​print((lambda x, y: x + y)(34))</​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 cuvintesunt 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 +
  
 +2.3.5. Write the signature and implement the following function:
 <code haskell> <code haskell>
-flip f = \a -> \b -> f b a +   ["​Matei",​ "​Mihai"​] ["​Popovici","​Dumitru"​] ​[("​Matei","​Popovici"​),​ ("​Mihai","​Dumitru"​)]
-      -- ^^^^^^^^^^^^+^^^^  +
-      --     f este o variabilă liberă în contextul funcției anonime de după egal+
 </​code>​ </​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.6. Implement the following function:
 <code haskell> <code haskell>
-mod_flipped ​flip mod+   f ["​Matei",​ "​Mihai"​] ["​Popovici","​Dumitru"​] ​["​MPopovici",​ "​MDumitru"​]
 </​code>​ </​code>​
  
-Alte exemple de închideri funcționale: +2.3.7. Write a function which takes a list of pairs - student-name and grade, removes those with grades less than 5, splits the name in substrings, and adds one bonus point to people with three names. Example:
 <code haskell> <code haskell>
-mod3 x = mod x 3 -- închidere funcțională peste funcția mod din Prelude și constanta 3 +    f [("Dan Matei Popovici",​9),("​Mihai",​4),("​Andrei Alex",​6)] = 
-plus5 x = x + 5  -- închidere funcțională peste funcția ​(+și constanta 5 +     [(["​Dan",​ "​Matei",​ "​Popovici"​],​10),(["Andrei", "​Alex"​],​6)]
-(+5            -- aceeași ca mai sus; +
-                 ​-- ​    (+este o funcție care primește 2 argumente și se evaluează la suma lor +
-                 --     (+5este închiderea funcțională rezultată în urma "hardcodării" ​unuia dintre argumente+
 </​code>​ </​code>​
  
-===== Hoogle ===== 
- 
-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. Scrieți o funcție ce primește un număr $ x$ și o listă de numere de forma $ [a_1, a_2, ... a_n]$ ca parametrii și calculează $ x - a_1 - a_2 - ... - a_n$. 
- 
-2. Inversați o listă folosind foldl. 
- 
-3. Definiți o închidere funcțională care prefixează [1,2,3] la o listă primită ca parametru. 
- 
-4. Scrieți o închidere funcțională care elimină caracterul '​a'​ dintr-un string primit ca parametru. 
-  * Scrieți o implementare ce utilizează o funcție de tip ''​fold''​ 
-  * Scrieți o alta care folosește funcția ''​filter''​ 
- 
-5. Implementați o funcție ce primește ca parametru o lista de string-uri și returnează concatenarea lor folosind fold. 
- 
-6. Se dau două cuvinte de aceeași lungime. Să se numere câte caractere sunt diferite în cele doua șiruri. 
- 
-7. Definiți o funcție de ordin superior care primește o funcție și un număr, și aplică de două ori funcția pe numărul respectiv. 
- 
-8. Definiți o funcție care primește un operator binar, și întoarce același operator în care ordinea parametrilor a fost inversată //(e.g. 1/3 -> 3/1)// 
- 
-9. Implementați și testați funcțiile: 
-  * foldl 
-  * foldr 
-  * map  
-  * filter 
-  * zipWith 
-  * compunerea de funcții (operatorul ''​.''​ din Haskell) 
- 
-<note tip> 
-Dacă nu cunoașteți vreuna dintre funcții, o puteți căuta pe Hoogle pentru a vedea tipul și scopul ei.  
-</​note>​ 
- 
-10. Implementați,​ folosind foldl sau foldr: 
-  * map 
-  * filter 
- 
-11. Implementați o funcție ce primește 2 parametri: un șir de cifre de forma "​111222111333112"​ și un număr K. Această funcție trebuie să facă următorii pași de K ori: 
-  * se grupează cifrele identice în felul următor "​111222111333112"​ -> ["​111",​ "​222",​ "​111",​ "​333",​ "​11",​ "​2"​] 
-  * pentru fiecare element din lista rezultată se generează un element de forma (numărul de apariții al cifrei din șir, cifra din șir) ex:  ["​111",​ "​222",​ "​111",​ "​333",​ "​11",​ "​2"​] -> [("​3",​ "​1"​),​ ("​3",​ "​2"​),​ ("​3",​ "​1"​),​ ("​3",​ "​3"​),​ ("​2",​ "​1"​),​ ("​1",​ "​2"​)] 
-  * se face flatten pe lista rezultată la pasul anterior ex: [("​3",​ "​1"​),​ ("​3",​ "​2"​),​ ("​3",​ "​1"​),​ ("​3",​ "​3"​),​ ("​2",​ "​1"​),​ ("​1",​ "​2"​)] ->  "​313231332112"​ 
- 
-<​note>​ 
-Pentru mai multe informații despre secvență: https://​en.wikipedia.org/​wiki/​Look-and-say_sequence 
-</​note>​ 
- 
-{{:​pp:​lab2-skel.zip|Laborator 2 - Schelet}}\\ 
  
  
-===== 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]]