Magasabbrendű függvények bevezetés

Szeletek

A szeletek operátorokból képzett (egyparaméteres) függvények, például (< 3), (^2), (4 *).

Test>
(^2) :: Num a => a -> a
Test>
9 :: Integer
Test>
8 :: Integer

A magasabbrendű függvényeket függvényekkel lehet paraméterezni. A szeletek jól használhatók magasabbrendű függvényekkel.

Megjegyzés. A (-4) a -4 számot jelenti. A néggyel csökkentés szelete a (+(-4)) kifejezés.

Feladat: Prelude.map [*]

Definiáljuk újra rekurzióval az elemenkénti feldolgozást!

map :: (a -> b) -> [a] -> [b]

Test>
[2, 3, 4, 5, 6] :: [Integer]
Test>
[1, 4, 9, 16, 25] :: [Integer]
Test>
[2, 4, 8, 16, 32] :: [Integer]
Test>
[1, 0, 1, 0, 1] :: [Integer]
Test>
[False, True, False, True, False] :: [Bool]

Megjegyzés. A map függvényt így is lehetne definiálni: map f l = [f e | e <- l].

Feladat: Prelude.filter [*]

Definiáljuk újra rekurzióval a szűrést!

filter :: (a -> Bool) -> [a] -> [a]

Test>
[2, 4, 6, 8, 10] :: [Integer]
Test>
[5, 6, 7, 8, 9, 10] :: [Integer]
Test>
[1, 2, 3] :: [Integer]
Test>
[1, 2, 4, 5, 4, 2, 1] :: [Integer]

Megjegyzés. A filter függvényt így is lehetne definiálni: filter p l = [e | e <- l, p e].

Feladat: Számlálás

Számoljuk meg egy adott tulajdonságú elem előfordulásait egy listában!

count :: (a -> Bool) -> [a] -> Int

Test>
2 :: Int
Test>
3 :: Int

Feladat: Prelude.takeWhile [*]

Definiáljuk újra a takeWhile függvényt!

takeWhile :: (a -> Bool) -> [a] -> [a]

Test>
[1, 2, 3, 4] :: [Integer]
Test>
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400, 441, 484] :: [Integer]

Feladat: Prelude.dropWhile

Definiáljuk újra a dropWhile függvényt!

dropWhile :: (a -> Bool) -> [a] -> [a]

Test>
[5, 6, 5, 4, 3, 2] :: [Integer]

Feladat: Prelude.span

Definiáljuk újra a span függvényt, amely lényegében a következő módon adható meg:

span p xs = (takeWhile p xs, dropWhile p xs)

Készítsünk ennél hatékonyabb definíciót!

span :: (a -> Bool) -> [a]{-véges-} -> ([a],[a])

Test>
([1, 2], [3, 4, 1, 2, 3, 4]) :: ([Integer], [Integer])
Test>
([1, 2, 3], []) :: ([Integer], [Integer])
Test>
([], [1, 2, 3]) :: ([Integer], [Integer])

Feladat: Prelude.iterate [*]

Definiáljuk újra az iterate függvényt!

iterate :: (a -> a) -> a -> [a]  

Test>
[1, 2, 4, 8, 16, 32, 64, 128, 256, 512] :: [Integer]

Megjegyzés. A függvény alkalmazásának eredménye egy végtelen lista.

Feladat: A dollár operátor [*]

Definiáljuk a dollár operátort!

infixr 0 $
($) :: (a -> b) -> a -> b

Test>
"abd" :: [Char]
Test>
-0.6080830096407656 :: Double

A dollár operátor használatával alapvetően zárójelpárokat spórolhatunk meg. De tulajdonképpen minden olyan esetben jól jön, amikor a függvényalkalmazás operátorát akarjuk explicite alkalmazni, például:

Test>

A dollár operátor jobbra kötő 0 erősségű operátor, ezért sin $ cos $ 4 + log 1 helyes zárójelezése sin $ (cos $ (4 + log 1)). A dollár definíciója szerint ,,nem csinál semmit’’, így a végeredmény sin (cos (4 + log 1)).

Megjegyzés. Az 1 + sin (2 + 3) helyett nem írhatjuk hogy 1 + sin $ 2 + 3, mert ez a következő (hibás) kifejezést rövidíti: (1 + sin) (2 + 3).

Feladat: Prelude.all

Definiáljuk újra az univerzális kvantort!

all :: (a -> Bool) -> [a]{-véges-} -> Bool

Test>
True :: Bool
Test>
True :: Bool
Test>
True :: Bool

Feladat: Prelude.any [*]

Definiáljuk újra az egzisztenciális kvantort!

any :: (a -> Bool) -> [a]{-véges-} -> Bool

Test>
True :: Bool
Test>
True :: Bool

Feladat: Prelude.elem

Definiáljuk újra az elem függvényt (az any segítségével), amely megállapítja, hogy egy érték szerepel-e egy listában!

elem :: Eq a => a -> [a]{-véges-} -> Bool

Test>
True :: Bool
Test>
True :: Bool

Feladat: Több elem szűrése

Szűrjünk ki adott elemeket egy listából!

filters :: Eq a => [a] -> [a] -> [a]

Test>
"Szia hogy vagy" :: [Char]

Megjegyzés. Van notElem függvény is.

Feladat: Prelude.zipWith [*]

Definiáljuk újra a zipWith függvényt!

zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]

Test>
[6, 8, 10, 12] :: [Integer]
Test>
[(1, 'h'), (2, 'e'), (3, 'l')] :: [(Integer, Char)]

A párok egyetlen konstruktora:

(,) :: a -> b -> (a, b)

Noha írhatjuk azt, hogy (,) 2 'c', ehelyett inkább (2,'c')-t szokás írni. Néha kifejezetten hasznos a (,) jelölés. Például a zip egy lehetséges definíciója:

Feladat: Különbségsorozat [*]

A zipWith felhasználásával készítsünk egy olyan függvényt, amely számok egy sorozatából előállítja azok páronkénti különbségeinek sorozatát!

differences :: Num a => [a] -> [a]

Test>
[1, 1, 1, 1] :: [Integer]
Test>
[-1, -1, -1, -1] :: [Integer]
Test>
[1, 3, 5, 7] :: [Integer]

Névtelen függvények

Matematikai minta: x ↦ x + 1 (hozzárendelési szabály)

Test>
Test>
Test>

A változó tetszőleges kisbetűs név lehet.


Az új változó elfedhet külső változókat vagy függvényeket:

Test>

Jó tanács: Névtelen függvényekben mindig egybetűs változóneveket használjunk.

Minden szelet egyszerűen átírható névtelen függvénnyé; a fordító is ezt teszi.

( +1) \x -> x + 1
( `mod`5) \x -> x `mod` 5
(2^) \x -> 2 ^ x

A szeletek használata mindenütt javasolt, ahol lehetséges.

Feladat: Fibonacci párok [*]

Állítsuk elő a (0,1), (1,1), (1,2), (2,3), (3,5), (5,8), … (végtelen) sorozatot! A sorozat előállítási szabálya: (a, b) ↦ (b, a+b).

fibPairs :: [(Integer, Integer)]

Test>
[(0, 1), (1, 1), (1, 2), (2, 3), (3, 5)] :: [(Integer, Integer)]
Test>
[0, 1, 1, 2, 3, 5, 8, 13] :: [Integer]

Feladat: Data.List.group

Adjuk meg a group függvény definícióját! Ez a függvény kisebb csoportokra bont egy listát aszerint, hogy az egymás mellett levő elemek megegyeznek vagy sem.

group :: Eq a => [a]{-véges-} -> [[a]]

Test>
[[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]] :: [[Integer]]
Test>
[[True, True, True, True]] :: [[Bool]]
Test>
["M", "i", "ss", "i", "ss", "i", "pp", "i"] :: [[Char]]

Segítség. Alkalmazzuk a span függvényt!

Feladat: Ismétlődő elemeket tartalmazó lista tömörítése

A compress l egymás után ismétlődő elemeket keresve tömörítse a paraméterként kapott listát!

compress :: Eq a => [a] -> [(Int,a)]

Test>
[(1, 'a'), (2, 'b'), (4, 'c'), (1, 'b')] :: [(Int, Char)]
Test>
[(5, 'a'), (2, 'b'), (1, 'd'), (1, 'k'), (2, 'r')] :: [(Int, Char)]

Segítség. Használhatjuk a group függvényt.

Feladat: Pascal-háromszög

Állítsuk elő a Pascal-háromszöget: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1],].

pascalTriangle :: [[Integer]]

Test>
[[1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1]] :: [[Integer]]

Feladat: Négyzetgyök számolás Newton-módszerrel

Közelítsük az a négyzetgyökét az \x->(x+a/x)/2 függvénnyel.

sqrt :: RealFloat a => a -> a

Test>
True :: Bool

Feladat: Prelude.uncurry [*]

Definiáljuk újra a uncurry függvényt, amely kétargumentumú függvényekből páron értelmezett függvényeket csinál!

uncurry :: (a -> b -> c) -> ((a, b) -> c)

Test>
"aa" :: [Char]

Megjegyzés. Ennek a fordítottja a curry függvény:

Test>
curry :: ((a, b) -> c) -> a -> b -> c
Test>
curry (uncurry (+)) :: Num c => c -> c -> c

Feladat: Kitömörítés [*]

Ismétlődő elemeket tartalmazó lista kitömörítése: az decompress legyen a compress inverze!

decompress :: Eq a => [(Int,a)] -> [a]

Test>
"abbccccb" :: [Char]

Feladat: Súlyozott összeg

Állítsunk elő súlyozott összeget! A súlyok az értékek elé vannak párosítva egy listában.

weightedSum :: Num a => [(a,a)] -> a

Test>
5 :: Integer