User Tools

Site Tools


laboratoare:02-functii-de-ordin-superior

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
laboratoare:02-functii-de-ordin-superior [2016/03/06 21:35]
calin.cruceru [Închideri funcționale]
laboratoare:02-functii-de-ordin-superior [2016/03/06 23:34] (current)
mihai.dumitru2201
Line 1: Line 1:
 ====== Funcții ====== ====== Funcții ======
  
-  * Semnificatia termenului //​aplicatie//​ +Responsabili laborator: ​ 
-  * Definirea **functiilor anonime** in Haskell: +  * [[mihai.dumitru@cti.pub.ro|Dumitru Mihai-Valentin]] 
-<code haskell> \x1 x2 ... -> expr </​code>​ +  * [[calin.cruceru@cti.pub.ro|Călin Cruceru]]
-  * Curry vs uncurry (si de ce nu conteaza in Haskell) +
-  * **Inchideri functionale**:​ +
-    * definiti o functie care prefixeaza ​[1,2,3la o lista primita ca parametru, ca o inchidere functionala+
  
 +Scopul laboratorului:​
 +  * Semnificația termenului //​aplicație//​
 +  * Definirea funcțiilor anonime în Haskell
 +  * Curry vs uncurry (și de ce nu contează în Haskell)
 +  * Închideri funcționale
 ===== Funcții de ordin superior ===== ===== Funcții de ordin superior =====
  
Line 14: Line 16:
 Pentru a înțelege importanța lor, vom da următorul exemplu: ne propunem să scriem două funcții care primesc o listă și returnează,​ într-o nouă listă Pentru a înțelege importanța lor, vom da următorul exemplu: ne propunem să scriem două funcții care primesc o listă și returnează,​ într-o nouă listă
   * toate elementele pare   * toate elementele pare
-  * toate elementele mai mari ca 10+  * toate elementele mai mari decât ​10
  
 <code haskell> <code haskell>
Line 65: Line 67:
 greaterThan10 = myFilter (> 10) greaterThan10 = myFilter (> 10)
 </​code>​ </​code>​
 +
 +
 +===== Currying vs. uncurrying =====  ​
 +
 +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.
 +
 +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>​
 +
 +Și în [[laboratoare:​01-functii-recursive#​definirea_unei_functii|laboratorul trecut]], am observat că nu există o separare între domeniu și codomeniu, de genul\\
 +''​(a -%%>%% 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 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>​
 +
 +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ț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:
 +
 +<code haskell>
 +testDiv5 x = x `mod` 5 == 0
 +multiplesOf5 = myFilter testDiv5
 +</​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''​.
 +
 +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).
 +
 +Rescriind funcția noastră, obținem:
 +<code haskell>
 +multiplesOf5 = myFilter (\x -> x `mod` 5 == 0)
 +</​code>​
 +
 +Următoarele expresii sunt echivalente:​
 +
 +<code haskell>
 +f x y = x + y
 +f x = \y -> x + y
 +f = \x y -> x + y
 +</​code>​
 +
 +<​note>​
 +Nu există vreo diferență între ''​\x y -%%>%% x + y''​ și ''​\x -%%>%% \y -%%>%% x + y''​.
 +</​note>​
  
 ===== Combinatori și închideri funcționale ===== ===== Combinatori și închideri funcționale =====
Line 70: Line 161:
 Un **combinator** este o funcție care nu se folosește de //variabile libere//​. ​ O variabilă este liberă în definiția unei funcții dacă nu se //referă// la niciunul dintre parametrii formali ai acesteia. Un **combinator** este o funcție care nu se folosește de //variabile libere//​. ​ O variabilă este liberă în definiția unei funcții dacă nu se //referă// la niciunul dintre parametrii formali ai acesteia.
  
-Exemple de combinatori:​ 
 <code haskell> <code haskell>
-let f1 = \a -> a +-- Exemple de combinatori 
-let f2 = \a -> \b -> a +f1 = \a -> a  
-let f3 = \f -> \a -> \b -> f b a+         -- prin f1 îi dăm un nume funcției anonime de după egal 
 +         -- această funcție anonimă are un parametru formal, "​a"​ 
 +         -- definiția ei conține doar evaluarea lui a 
 +         -- este un combinator deoarece nu folosește nicio altă variabilă în afară de a 
 + 
 +f2 = \a -> \b -> a 
 +f3 = \f -> \a -> \b -> f b a 
 + 
 +-- Exemple de "​ne-combinatori"​ 
 +f1_no = \a -> a + x 
 +  where x = some_other_expr 
 +  -- x este o variabilă liberă în contextul definiției funcției anonime de după egal 
 + 
 +-- Pentru mai multe exemeple, continuă să citești
 </​code>​ </​code>​
  
Line 82: Line 185:
  
 <code haskell> <code haskell>
-let flip = \f -> \a -> \b -> f b a+flip = \f -> \a -> \b -> f b a -- was f3
 </​code>​ </​code>​
  
Line 88: Line 191:
  
 <code haskell> <code haskell>
-let flip f = \a -> \b -> f b a +flip f = \a -> \b -> f b a 
-          -- ^^^^^^^^^^^^+^^^^  +      -- ^^^^^^^^^^^^+^^^^  
-          --     f este o variabilă liberă în contextul funcției anonime de după egal+      --     f este o variabilă liberă în contextul funcției anonime de după egal
 </​code>​ </​code>​
  
-Ce ne returnează funcție 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 2 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.+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 2 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.
  
 <code haskell> <code haskell>
-let mod_flipped = flip mod+mod_flipped = flip mod
 </​code>​ </​code>​
  
Line 114: Line 217:
 Puteți găsi și alte exemple de închideri funcționale care se găsesc în acest suport de laborator? Puteți găsi și alte exemple de închideri funcționale care se găsesc în acest suport de laborator?
 </​note>​ </​note>​
-===== Currying vs. uncurrying =====  ​ 
- 
-Currying este o tehnică (numită după [[https://​en.wikipedia.org/​wiki/​Haskell_Curry|tizul Haskell-ului]]) prin care, dintr-o funcție care ia mai multe argumente, se obține o secvență de funcții care iau un singur argument. 
- 
-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''​. 
- 
-===== Funcții anonime ===== 
-''​TODO''​ 
- 
- 
 ===== Exerciții ===== ===== Exerciții =====
  
-1. 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.+1. Definiți o închidere ​funcțională ​care prefixează [1,2,3] la o listă primită ca parametru.
  
-2. Definiți o funcție care primește un operator binar, și întoarce același operator în care ordinea parametrilor a fost inversată //(e.g1/3 -> 3/1)//+2. 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.
  
-3. Implementați și testațfuncțiile:+3. Definiți o funcție care primește un operator binar, ​și întoarce acelașoperator în care ordinea parametrilor a fost inversată //(e.g. 1/3 -> 3/1)//
  
-a) foldl +4. Implementați și testați funcțiile:​ 
- +  - foldl 
-b) foldr +  ​- ​foldr 
- +  ​- ​map 
-c) map +  ​- ​filter 
- +  ​- ​zipWith 
-d) filter +  ​- ​compunerea de funcții (operatorul ''​.''​ din Haskell)
- +
-e) zipWith +
- +
-f) compunerea de funcții (operatorul ''​.''​ din Haskell)+
  
 <note tip> <note tip>
Line 150: Line 237:
 </​note>​ </​note>​
  
-4. Implementați,​ folosind foldl sau foldr: +5. Implementați,​ folosind foldl sau foldr: 
- +  ​- ​map 
-a) map +  ​- ​filter
- +
-b) filter +
 ===== Referințe ===== ===== Referințe =====
   * [[https://​www.haskell.org/​hoogle/​|Hoogle - motor de căutare pentru funcții Haskell]]   * [[https://​www.haskell.org/​hoogle/​|Hoogle - motor de căutare pentru funcții Haskell]]
laboratoare/02-functii-de-ordin-superior.1457292904.txt.gz · Last modified: 2016/03/06 21:35 by calin.cruceru