하스켈 프로그래밍 입문 3
TRANSCRIPT
하스켈 프로그래밍입문 3하스켈 학교 2016 년 5 월 28 일서광열
Identity, State, Writer Monad
Term
data Term = Con Int | Div Term Term
변종 0: Basic Evaluator
eval :: Term -> Int
eval (Con a) = a
eval (Div t u) = eval t `div` eval u
변종 1: 예외 처리data M a = Raise Exception | Return a
type Exception = String
변종 1: 예외 처리eval :: Term -> M Int
eval (Con a) = Return a
eval (Div t u) = case eval t of
Raise e -> Raise e
Return a ->
case eval u of
Raise e -> Raise e
Return b ->
if b == 0
then Raise “divide by zero”
else Return (a `div` b)
변종 2: division 카운터type M a = State -> (a, State)
type State = Int
변종 2: division 카운터eval :: Term -> M Int
eval (Con a) x = (a, x)
eval (Div t u) x = let (a, y) = eval t x in
let (b, z) = eval u y in
(a `div` b, z + 1)
변종 3: 실행 과정 출력type M a = (Output, a)
type Output = String
변종 3: 실행 과정 출력eval :: Term -> M Int
eval @term(Con a) = (line term a, a)
eval @term(Div t u) = let (x, a) = eval t in
let (y, b) = eval u in
(x ++ y ++ line term (a `div` b), a `div` b)
line :: Term -> Int -> Output
line t a = “eval (“ ++ show t ++ “) <-” ++ show a ++ “\n”
Monadic Evaluatoreval :: (Moand m) => Term -> m Int
eval (Con a) = return a
eval (Div t u) = do a <- eval t
b <- eval u
return (a `div` b)
Monadic Evaluatoreval :: (Moand m) => Term -> m Int
eval (Con a) = return a
eval (Div t u) = do a <- eval t
b <- eval u
return (a `div` b)
변종 0: Basic Evaluator
newtype M a = I a
instance Monad M where
return a = I a
(I a) >>= k = k a
변종 1: 예외 처리data M a = Raise Exception | Return a
type Exception = String
instance Monad M where
return a = Return a
m >>= k = case m of
Raise e -> Raise e
Return a -> k a
raise :: Exception -> M a
raise e = Raise e
변종 1: 예외 처리eval :: Term -> M Int
eval (Con a) = return a
eval (Div t u) = do a <- eval t
b <- eval u
if b == 0
then raise “divide by zero”
else return (a `div` b)
변종 2: division 카운터type M a = State -> (a, State)
type State = Int
instance Monad M where
return a = \x -> (a, x)
m >>= k = \x -> let (a, y) = m x in
let (b, z) = k a y in
(b, z)
tick :: M ()
tick = \x -> ((), x + 1)
변종 2: division 카운터eval :: Term -> M Int
eval (Con a) = return a
eval (Div t u) = do a <- eval t
b <- eval u
tick
return (a `div` b)
변종 3: 실행 과정 출력type M a = (Output, a)
type Output = String
instance Monad m where
return a = (“”, a)
m >>= k = let (x, a) = m in
let (y, b) = k a in
(x ++ y, b)
out :: Output -> M ()
out x = (x, ())
변종 3: 실행 과정 출력eval :: Term -> M Int
eval term@(Con a) = do out (line term a)
return a
eval term@(Div t u) = do a <- eval t
b <- eval u
out (line term (a `div` b))
return (a `div` b)
Control.Monad• mapM :: Monad m => (a -> m b) -> [a] -
> m [b]
• forM :: Monad m => [a] -> (a -> m b) -> m [b]
• sequence :: Monad m => [m a] -> m [a]
Note: [t] generalizes to Traversable t
Functional Parser
출처 : http://www.willamette.edu/~fruehr/haskell/seuss.html
Parsernewtype Parser a = Parser (String -> [a, String])
• empty 리스트는 실패 , 아니면 성공• (a, s) 에서 a 는 파싱한 결과 s 는 파싱하고 남은 문자열• 리스트를 리턴하여 ambiguous grammar 처리
item
• 문자열이 empty 가 아니면 첫 번째 문자를 리턴• empty 이면 에러
item :: Parser Char
item = Parser (\cs -> case cs of
"" -> []
(c:cs) -> [(c, cs)])
모나드 정의class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
instance Monad Parser where
return a = (Parser (\cs -> [(a, cs)])
p >>= f = (Parser (\cs -> concat [parse (f a) cs’ |
(a, cs’) <- parse p cs])
parse (Parser p) = p
모나드 법칙1. return a >> f = f a
2. p >>= return = p
3. p >>= (\a -> (f a) >>= g)) = (p >>= (\a -> f a)) >>= g
Do 표기법p1 >>= \a1 ->
p2 >>= \a2 ->
…
pn >>= \an ->
f a1 a2 … an
do a1 <- p1
a2 <- p2
…
an <- pn
f a1 a2 … an
Do 표기법 사용 예제p :: Parser (Char, Char)
p = do c <- item
item
d <- item
return (c, d)
MonadZero, MonadPlus
class Monad m => MonadZero m where
zero :: m a
class MonadZero m => MonadPlus m where
(++) :: m a -> m a -> m a
Choice Operatorinstance MonadZero Parser where
zero = Parser (\cs -> [])
instance MonadPlus Parser where
p ++ q = (Parser (\cs ->
parse p cs ++ parse q cs))
법칙• zero ++ p = p
• p ++ zero = p
• p ++ (q ++ r) = (p ++ q) ++ r
• zero >>= f = zero
• p >>= const zero = zero
• (p ++ q) >>= f = (p >>= f) ++ (q >>= f)
• p >>= (\a -> f a ++ g a) = (p >>= f) ++ (q >>= g)
Deterministic Choice Operator
(+++) :: Parser a -> Parser a -> Parser a
p +++ q = Parser (\cs -> case parse (p ++ q) of
[] -> []
(x:xs) -> [x])
Conditional Parsingsat :: (Char -> Bool) -> Parser Char
sat p = do c <- item
if p c then return c
else zero
char :: Char -> Parser Char
char c = sat (c ==)
재귀 Combinatorstring :: String -> Parser String
string "" = return ""
string (c:cs) = do char c
string cs
return (c:cs)
재귀 Combinatormany :: Parser a -> Parser [a]
many p = many1 p +++ return []
many1 :: Parser a -> Parser [a]
many1 p = do a <- p
as <- many p
return (a:as)
재귀 Combinatorsepby :: Parser a -> Parser b -> Parser [a]
p `sepby` q = (p `sepby1` q) +++ return []
sepby1 :: Parser a -> Parser b -> Parser [a]
p `sepby1` q = do a <- p
as <- many (do {sep; p})
return (a:as)
재귀 Combinatorchainl :: Parser a -> Parser (a -> a -> a) -> a -> Parser a
chainl p op a = (p `chainl1` op) +++ return a
chainl1 :: Parser a -> (Parser a -> a -> a) -> Parser a
p `chainl1` op = do { a <- p; rest a }
where rest a = (do f <- op
b <- p
return f a b)
+++ return a
Lexical Combinatorsspace :: Parser String
space = many (sat isSpace)
token :: Parser a -> Parser a
token p = do a <- p
space
return a
Lexical Combinatorssymb :: String -> Parser String
symb cs = token (string cs)
apply :: Parser a -> String -> [(a, String)]
apply p = parse (do {space; p})
Expression Grammar• expr ::= expr addop term | term
• term ::= term mulop factor | factor
• factor ::= digit | (expr)
• digit ::= 0 | 1 | … | 9
• addop ::= + | -
• mulop ::= * | /
Expression Parserexpr :: Parser Int
addop :: Parser (Int -> Int -> Int)
mulop :: Parser (Int -> Int -> Int)
expr = term `chainl1` addop
term = factor `chainl1` mulop
factor = digit +++ do { symb "("; n <- expr; symb ")"; return n }
digit = do { x <- token (sat isDigit); return (ord x - ord ‘0’)}
addop = do { symb "+"; return (+)} +++ do {symb "-"; return (-)}
mulop = do { symb "*"; return (*)} +++ do {symb "/"; return (div)}
Expression Parser
> apply expr " 1 - 2 * 3 + 4 "
[(-1, "")]
Library
1. parsec
2. megaparsec
3. attoparsec
Zipper
Tree
data Tree a = Empty
| Node a (Tree a) (Tree a)
deriving (Show)
Tree 업데이트changeToP :: Tree Char -> Tree Char
changeToP (Node x l (Node y (Node _ m n) r)) =
Node x l (Node y (Node 'P' m n) r)
Tree 업데이트data Direction = L | R deriving (Show)
type Directions = [Direction]
changeToP :: Directions -> Tree Char -> Tree Char
changeToP (L:ds) (Node x l r) = Node x (changeToP ds l) r
changeToP (R:ds) (Node x l r) = Node x l (changeToP ds r)
changeToP [] (Node _ l r) = Node 'P' l r
elementelemAt :: Directions -> Tree a -> a
elemAt (L:ds) (Node _ l _) = elemAt ds l
elemAt (R:ds) (Node _ _ r) = elemAt ds r
elemAt [] (Node x _ _) = x
예제> let newTree = changeToP [R,L] freeTree
> elemAt [R,L] newTree
'P'
Breadcrumbstype Breadcrumbs = [Direction]
goLeft :: (Tree a, Breadcrumbs) -> (Tree a, Breadcrumbs)
goLeft (Node _ l _, bs) = (l, L:bs)
goRight :: (Tree a, Breadcrumbs) -> (Tree a, Breadcrumbs)
goRight (Node _ _ r, bs) = (r, R:bs)
Breadcrumbs> goLeft (goRight (freeTree, []))
(Node 'W' (Node 'C' Empty Empty) (Node 'R' Empty Empty),[L,R])
Zipper 정의data Crumb a =
LeftCrumb a (Tree a)
| RightCrumb a (Tree a)
deriving (Show)
type Breadcrumbs a = [Crumb a]
Zipper 함수goLeft :: (Tree a, Breadcrumbs a)
-> (Tree a, Breadcrumbs a)
goLeft (Node x l r, bs) = (l, LeftCrumb x r:bs)
goRight :: (Tree a, Breadcrumbs a)
-> (Tree a, Breadcrumbs a)
goRight (Node x l r, bs) = (r, RightCrumb x l:bs)
Zipper 함수goUp :: (Tree a, Breadcrumbs a)
-> (Tree a, Breadcrumbs a)
goUp (t, LeftCrumb x r:bs) =
(Node x t r, bs)
goUp (t, RightCrumb x l:bs) =
(Node x l t, bs)
Zipper 정의
type Zipper a = (Tree a, Breadcrumbs a)
Zipper 함수modify :: (a -> a)
-> Zipper a
-> Zipper a
modify f (Node x l r, bs) =
(Node (f x) l r, bs)
modify f (Empty, bs) = (Empty, bs)
Zipper 함수topMost :: Zipper a -> Zipper a
topMost (t,[]) = (t,[])
topMost z = topMost (goUp z)
Zipper 사용 예> let newFocus = modify (\_ -> 'P') (goRight (goLeft (freeTree,[])))
> let newFocus2 = modify (\_ -> 'X') (goUp newFocus)
리스트data List a =
Empty
| Cons a (List a)
deriving (Show, Read, Eq, Ord)
리스트 Zipper 의 정의
type ListZipper a = ([a],[a])
리스트 Zipper 함수goForward :: ListZipper a -> ListZipper a
goForward (x:xs, bs) = (xs, x:bs)
goBack :: ListZipper a -> ListZipper a
goBack (xs, b:bs) = (b:xs, bs)
리스트 Zipper 사용 예> let xs = [1,2,3,4]
> goForward (xs,[])
([2,3,4],[1])
> goForward ([2,3,4],[1])
([3,4],[2,1])
> goForward ([3,4],[2,1])
([4],[3,2,1])
> goBack ([4],[3,2,1])
([3,4],[2,1])
참고 자료1. Monads for functional programming
• http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf
2. Monadic Parsing in Haskell• http://www.cs.nott.ac.uk/~pszgmh/pearl.pdf
3. Learn You a Haskell for Great Good!• http://learnyouahaskell.com/zippers