Ćwiczymy funkcje wyzszego rzedu
incAll :: [[Int]] -> [[Int]]
która zwiększy o 1 każdy element każdego elementu swojego argumentu, np
*Main Data.List> incAll $ inits [1..3] [[],[2],[2,3],[2,3,4]]
concat :: [[a]] -> [a]
Rozważmy typ drzew troche inny niz na wykladzie
Tree a = Empty | Node a (Tree a) (Tree a) deriving (Eq, Ord, Show)
-- class Functor f where -- fmap :: (a -> b) -> f a -> f b instance Functor Tree where... *Tree> fmap (+1) $ Node 1 Empty Empty Node 2 Empty Empty
toList :: Tree a -> [a]
ktora zamieni drzewo w liste elementow drzewa (w porzadku infiksowym)
insert :: (Ord a) => a -> Tree a -> Tree a contains :: (Ord a) -> a -> Tree a -> Bool fromList :: (Ord a) => [a] -> Tree a
instance Functor (Either e) where -- fmap :: (a -> b) -> Either e a -> Either e b
reverseRight :: Either e [a] -> Either e [a]
odwracajaca liste zawarta w Right
(dwie wersje, bezposrednio i z uzyciem fmap)
class Functor f => Pointed f where pure :: a -> f a
i jej instancje dla list, Maybe, Tree
Importy i moduły biblioteczne, np. przecwiczyć http://learnyouahaskell.com/modules
readInts :: String -> [Int]
która odczyta z napisu występujące w nim liczby naturalne, np
*Main> readInts "1 23 456 7.8 abc 9" [1,23,456,9] *Main> readInts "foo" []
użyj funkcji isDigit
z modulu Data.Char
oraz funkcji map
, filter
, all
z Prelude
readInts2 :: String -> Either String [Int]
która da listę liczb, jeśli wszystkie slowa jej argumentu są liczbami a komunikat o błędzie w przeciwnym przypadku
Może sie przydać funkcja reverseRight
z zad. 3b (lub fmap
dla Either
)
*Main> readInts2 "1 23 456 foo 9" Left "Not a number: foo" *Main> readInts2 "1 23 456" Right [1,23,456]
sumInts :: String -> String
stwórz program uruchamiający funkcję sumInts przy pomocy interact.
Rozważmy typ dla wyrażeń arytmetycznych z let:
data Exp = IntE Int -- stała całkowita | OpE Op Exp Exp -- operacja, np e1 + e2 | VarE String -- zmienna | LetE String Exp Exp -- let var = e1 in e2 type Op = (Int -> Int -> Int, String) -- (działanie, nazwa)
testExp2 :: Exp testExp2 = (2 + 2) * 3
(metody abs i signum mogą mieć wartość undefined)
W poprzednim tygodniu pisaliśmy funkcje
readInts2 :: String -> Either String [Int] sumInts :: String -> String
Przepisz funkcje readInts2, sumInts na notację monadyczną
(Uwaga: w tym zadaniu raczej nadal tworzymy funkcje String -> String i korzystamy z interact niz bezposrednio z IO, chyba ze ktos bardzo chce)
Uzupełnić przykład z wykładu:
data ParseError = Err {location::Int, reason::String} instance Error ParseError ... type ParseMonad = Either ParseError parseHexDigit :: Char -> Int -> ParseMonad Integer parseHex :: String -> ParseMonad Integer toString :: Integer ->; ParseMonad String -- convert zamienia napis z liczba szesnastkowa -- na napis z liczba dziesietna convert :: String -> String convert s = str where (Right str) = tryParse s `catchError` printError tryParse s = do {n <- parseHex s; toString n} printError e = ...
Napisz własną implementację funkcji
sequence :: Monad m => [m a] -> m [a] mapM :: Monad m => (a -> m b) -> [a] -> m [b] forM :: Monad m => [a] -> (a -> m b) -> m [b]
Nieco inny od monad model obliczeń reprezentuje klasa Applicative:
class Functor f => Applicative f where pure :: a -> f a (>*>) :: f(a->b) -> f a -> f b
pure
to to samo co return
(<*>)
reprezentuje sekwencjonowanie obliczeń podobne do (=<<)
pure = return mf <*> ma = do { f <- mf; a <- ma; return mf ma }
Applicative
dla Maybe
i Either
*>
będącą analogiem >>
(czyli ignorującą wynik pierwszego obliczenia):(*>) :: f a -> f b -> f b
(<*) :: f a -> f b -> f a
Applicative
zamiast Monad
Funkcja
allPairs :: [a] -> [a] -> [[a]] allPairs xs ys = [[x,y] | x <- xs, y <- ys]
daje listę wszystkich list dwuelementowych, gdzie pierwszy element należy do pierwszego argumentu, drugi do drugiego, np.
*Main> allPairs [1,2,3] [4,5] [[1,4],[1,5],[2,4],[2,5],[3,4],[3,5]]
allCombinations :: [[a]] -> [[a]]
, która dla n-elementowej listy list da listę wszystkich n-elementowych list takich, że i-ty element należy do i-tego elementu argumentu, np
Main> allCombinations [[1,2], [4,5], [6], [7]]
[[1,4,6,7],[1,5,6,7],[2,4,6,7],[2,5,6,7]]
a. data Tree a = Empty | Node a (Tree a) (Tree a) deriving (Eq, Ord, Show)
Napisz funkcję
renumber :: Tree a -> Tree Int
która dla danego drzewa da drzewo, gdzie w każdym węźle przechowywana będzie głębokość tego węzła (odległość od korzenia).
Porównaj rozwiązania z użyciem monady Reader i bez.
b. Dane typy dla wyrażeń arytmetycznych
data Exp = IntE Int | OpE Op Exp Exp | VarE String | LetE String Exp Exp -- let var = e1 in e2 type Op = Int -> Int -> Int
Napisz funkcję
evalExp :: Exp -> Int
ktora obliczy wartość takiego wyrażenia, np
-- -- let x = -- let y = 5 + 6 -- in y / 5 -- in x * 3 -- -- ==> 6 -- test = LetE "x" (LetE "y" (OpE (+) (IntE 5) (IntE 6)) (OpE div y (IntE 5))) (OpE (*) x (IntE 3)) where x = VarE "x" y = VarE "y"
data Tree a = Empty | Node a (Tree a) (Tree a) deriving (Eq, Ord, Show)
Napisz funkcję
renumberTree :: Tree a -> Tree Int
która ponumeruje wezly drzewa tak, ze kazdy z nich bedzie mial inny numer. Porownaj rozwiazania z uzyciem monady State i bez.
możliwe dodatkowe wymaganie: ponumeruj wezly drzewa w kolejnosci infiksowej. (toList $ renumber $ fromList "Learn Haskell") == [0..12]
newtype IdentityT = ... instance (Functor m) => Functor (IdentityT m) where instance (Monad m) => Monad (IdentityT m) ... instance MonadTrans IdentityT ... instance MonadPlus m => MonadPlus (IdentityT m) ...
instance MonadPlus (MaybeT m) ...
Dana składnia abstrakcyjna wyrażeń arytmetycznych (jak w 3. tygodniu)
data Exp = IntE Int | OpE Op Exp Exp | VarE String | LetE String Exp Exp -- let var = e1 in e2 type Op = Int -> Int -> Int
a. zaprojektuj składnię konkretną
Sugestie: standardowa notacja infiksowa oraz notacja prefiksowa a la Lisp: (* (+ 1 2) 3)
b. napisz parser dla tej składni przy uzyciu Text.ParserCombinators.Parsec
UWAGA: Ze względów wydajnościowych, operator (<|>) z biblioteki Parsec jest prawie deterministyczny i nie będzie działać dobrze dla produkcji, które mają wspólny (niepusty) prefiks.
Możemy odzyskać niedeterminizm przy pomocy kombinatora try, np.
try p<|> q
Napisz własne wersje kombinatorów parsujących użytych w poprzednim zadaniu
Sugestie:
newtype Parser a = Parser { runParser :: String -> [(a,String)] } newtype Parser a = Parser { runParser :: String -> [Either ErrorMessage (a,String)] }
albo, używając transformatorów monad
type Parser a = StateT [Char] (ErrorT String []) a
Napisz parser dla składni konkretnej wyrażeń zaprojektowanej w poprzednim tygodniu przy uzyciu BNFC (lub, jeśli ktoś bardzo chce, innego generatora parserów).
Składnia abstrakcyjna może być nieco inna (np. BNFC w pewnym sensie narzuca składnię abstrakcyjną).