Skip to content

Latest commit

 

History

History
255 lines (187 loc) · 4.34 KB

Lecture03.md

File metadata and controls

255 lines (187 loc) · 4.34 KB

Programowanie funkcyjne

Tomasz Brengos

Wykład 3

Kod wykładu

Basics/Lecture03.hs


Typeclasses

W REPL:

ghci> :t (==)

ghci> :t show

ghci> :t (<)

ghci> :t read

ghci> :t fmap

Definiowanie własnych instancji

data List a = Empty | Head a (List a)

instance Show a => Show List a where
  ...

instance Eq a => Eq (List a) where
  ...

instance Functor List where
  ...

Definiowanie własnych typeclasses

Przyklady typeclass predefiniowanych w Haskellu (wersja skrócona):

class Functor f where
  fmap :: (a -> b) -> f a -> f b 

class Show a where
  show :: a -> String

class Semigroup a where
  (<>) :: a -> a -> a

class Semigroup a => Monoid a where
  mempty  :: a
  mappend :: a -> a -> a
  mappend = (<>)

Rozszerzmy nasze definicje instancji dla List

instance Semigroup (List a) where
...

instance Monoid (List a) where
...

Funktory

Ćwiczenia:

data Maybe1 a = Just a | Nothing -- Maybe a := a + {*}

instance Functor Maybe1 where
  fmap = ...
data Either1 a b = Left a | Right b

instance Functor (Either1 a) where
  fmap = ...

Folds (klasycznie)

Niech:

(*)  :: a -> b -> b
(#)  :: b -> a -> b
seed :: b
ai   :: a

Rozważmy następujące przyporządkowania:

foldl (#) seed [a1..an] -> ((..(seed#a1)#a2#..)#an
foldr (*) seed [a1..an] -> a1*(a2*..(an*seed))..) 

Ich definicje są następujące:

foldl :: (b -> a -> b) -> b -> [a] -> b
foldl f seed []     =  seed
foldl f seed (x:xs) =  foldl f (f seed x) xs

oraz:

foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f seed []     = seed
foldr f seed (x:xs) = f x (foldr seed xs)

Obie funkcje działają leniwie! Jedną z nich można przepisać do wersji gorliwej:

foldl' f seed []     = seed
foldl' f seed (x:xs) = let z = f seed x in seq z (foldl' f z xs)

Folds (klasycznie)

Ćwiczenia

  1. Używając funkcji foldr zdefiniowac funkcję
init :: [a] -> [[a]] 

zwracającą wszystkie prefiksy argumentu, np.

init "tomek" = [[],"t","to","tom","tome","tomek"]
  1. Używając foldl zdefiniować
approxE :: Int -> Double

która dla argumentu n zwraca przybliżoną wartość liczby Eulera (używajac klasycznego wzoru na sumę odwrotności silnii kolejnych liczb nautralnych)

Ćwiczenia ciekawsze

  1. Zapisać foldl używajac funkcji foldr.

Ćwiczenie 3 (definicja foldl za pomocą foldr)

Komentarz pomocniczy:

foldl (#) seed [a1..an] -> ((..(seed#a1)#a2#..)#an
foldr (*) seed [a1..an] -> a1*(a2*..(an*seed))..) 

Rozważmy:

f1 = \v -> v # a1
f2 = \v -> v # a2
...
fn = \v -> v # an

Co się stanie jak policzymy:

(foldr (flip (.)) id [f1..fn]) seed   -- flip :: (a -> b -> c) -> (b -> a -> c) 
(foldr (flip (.)) id [f1..fn]) seed ->   (...( id . fn )..). f2) . f1 seed 
== (fn . fn-1 . .. . f1)  seed -> (.. (seed # a1) # a2 .. )# an  

Dokończyć zadanie!


Foldables bardziej ogólnie

data Tree a = EmptyTree | Leaf a | Node a (Tree a) (Tree a)

Przykład:

tree :: Tree String
tree = Node "a" (Node "b" Empty (Leaf "c")) (Node "d" Empty Empty)

Żeby łatwiej zrozumieć tree spójrzmy na:

          a
        /   \
       b     d
        \
         c

Spróbujmy napisać wersje foldr dla Tree a zamiast dla [a].

foldr :: (a -> b -> b) -> b -> Tree a -> b
foldr f seed EmptyTree           = seed
foldr f seed (Leaf x)            = f x seed
foldr f seed (Node x left right) = foldr f (f x (foldr f seed right)) left

Możemy też napisać coś nowego!

foldMap :: (Monoid m) => (a -> m) -> Tree a -> m

Okazuje się, że używając foldr możemy wyrazić foldMap i odwrotnie! Oznacza to, że wystarczy zdefiniować jedno z nich.

Ćwiczenie

Zdefiniować foldMap za pomocą foldr i odwrotnie (dla list).


Foldable typeclass

class Foldable t where
  foldr   :: (a -> b -> b) -> b -> t a -> b
  foldMap :: (Monoid m) => (a -> m) -> t a -> m
-- i inne:  
  fold    :: (Monoid m) => t m -> m
  foldl   :: (b -> a -> b) -> b -> t a -> b
  ...

Nie trzeba definiować obu: foldr i foldMap. Wystarczy jedno z nich, gdyż minimalna definicja instancji Foldable zawierać ma definicję

foldr
-- lub
foldMap