Edit this page Backlinks This page is read only. You can view the source, but not change it. Ask your administrator if you think this is wrong. ====== Aplicații cu funcții de ordin superior ====== Scopul laboratorului este de a exersa lucrul cu funcții de ordin superior. ===== Recapitulare ===== O **funcție de ordin superior** este //o funcție care primește ca argument sau returnează o funcție//. <code haskell> applyTwice f x = f (f x) adder x = (+x) </code> ==== foldr & foldl ==== 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> Prelude> foldl (^) 1 [2, 2, 2] -- (((1 ^ 2) ^ 2) ^ 2) 1 Prelude> foldr (^) 1 [2, 2, 2] -- (2 ^ (2 ^ (2 ^ 1))) 16 Prelude> </code> Dacă operația aleasă este [[https://en.wikipedia.org/wiki/Commutative_property|comutativă]] și [[https://en.wikipedia.org/wiki/Associative_property|asociativă]], rezultatul vor fi aceleași, doar ordinea operațiilor fiind diferită. Observați că și tipul funcției primită ca argument diferă; ''foldl'' trimite elementele listei ca al doilea parametru, iar ''foldr'' ca prim parametru. <code haskell> foldr (:) [] l -- întoarce o lista identică cu l foldl (flip (:)) [] l -- întoarce inversul listei l </code> Tipurile acestor funcții sunt, deci: <code> foldl :: (b -> a -> b) -> b -> [a] -> b foldr :: (a -> b -> b) -> b -> [a] -> b </code> Contemplați aceste tipuri până vă este clar de ce sunt așa. <note> Î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> Prelude> :t foldl foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b Prelude> :t foldr foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b </code> (Mai multe detalii despre notații ca ''%%=%%>'' și ''t a'' la cursurile/laboratoarele viitoare). </note> === Tail-recursion și evaluare leneșă === O privire la implementarea celor două folduri: <code haskell> foldl _ e [] = e foldl f e (x:xs) = foldl f (f e x) xs foldr _ e [] = e foldr f e (x:xs) = f x (foldr f e xs) </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șă. === Vizualizare === Următoarea este o vizualizare a rezultatului următoarelor două aplicări (pentru oricare ''f'' și ''z''): <code> foldr f z [1, 2, 3, 4, 5] foldl f z [1, 2, 3, 4, 5] </code> {{ :pp:fold-visualization.png?800 |}} Alte funcții de ordin superior des întâlnite: ''map'', ''filter'', ''zipWith'', ''flip''. ===== Exerciții ===== - 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]]