Ć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 MonadFunkcja
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 -> IntNapisz 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 -> Inta. 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ą).