【해답 예】Haskell Maybe 모나드 초입문

【해답 예】Haskell Maybe 모나드 초입문

2022-10-04 last update

15 minutes reading 하스켈
Haskell Maybe 모나드 초입문의 해답 예입니다.

피보나치 수



【질문1】다음의 함수 fib로 에러를 회피하기 위해 Maybe 모나드로 재기록해 주세요.
import Control.Applicative

fib 0 = Just 0
fib 1 = Just 1
fib n | n > 1     = (+) <$> fib (n - 2) <*> fib (n - 1)
      | otherwise = Nothing

main = do
    print $ fib (-1)
    print $ fib 6

실행 결과
Nothing
Just 8

재구성



바인드



【질문 2】 Maybe 모나드를 다루는 bind를 구현하십시오.
(Just a) `bind` b = b a
Nothing  `bind` _ = Nothing

main = do
    print $ Just 1 `bind` \a -> Just $ a * 2
    print $ Just 1 `bind` \a -> Nothing `bind` \b -> Just $ a * b

실행 결과
Just 2
Nothing

mapMaybe



재귀



【질문 3】 mapMaybe 를 재귀로 재실장해, 방금 전의 샘플로 검증해 주세요.
import Control.Applicative

mapMaybe _ []     = []
mapMaybe f (x:xs) = case f x of
    Just y  -> y : mapMaybe f xs
    Nothing ->     mapMaybe f xs

fact 0 = Just 1
fact n | n > 0     = (n *) <$> fact (n - 1)
       | otherwise = Nothing

facts n = ( map      fact [n, n - 1, n - 2]
          , mapMaybe fact [n, n - 1, n - 2]
          )

main = do
    print $ facts 3
    print $ facts 2
    print $ facts 1  -- cで失敗
    print $ facts 0  -- bで失敗

실행 결과
([Just 6,Just 2,Just 1],[6,2,1])
([Just 2,Just 1,Just 1],[2,1,1])
([Just 1,Just 1,Nothing],[1,1])
([Just 1,Nothing,Nothing],[1])

foldr



【문제 4】문제 3의 해답을 foldr 로 재작성해 주세요.
import Control.Applicative

mapMaybe f = (`foldr` []) $ \x xs -> case f x of
    Just x' -> x':xs
    Nothing ->    xs

fact 0 = Just 1
fact n | n > 0     = (n *) <$> fact (n - 1)
       | otherwise = Nothing

facts n = ( map      fact [n, n - 1, n - 2]
          , mapMaybe fact [n, n - 1, n - 2]
          )

main = do
    print $ facts 3
    print $ facts 2
    print $ facts 1  -- cで失敗
    print $ facts 0  -- bで失敗

실행 결과
([Just 6,Just 2,Just 1],[6,2,1])
([Just 2,Just 1,Just 1],[2,1,1])
([Just 1,Just 1,Nothing],[1,1])
([Just 1,Nothing,Nothing],[1])

문자열 검사



【문5】문자열s이 「x 문자의 연속한 숫자+ y 문자의 연속한 대문자」로 구성되는지 판정하는 함수numUpper x y s를 구현해 주세요. 성공하면 Just s, 실패하면 Nothing를 반환합니다. 불필요한 문자는 포함하지 않는 것으로 합니다.
import Data.Char
import Control.Monad

numUpper x y s = do
    guard $ length s == x + y
    guard $ length (filter isDigit $ take x s) == x
    guard $ length (filter isUpper $ drop x s) == y
    Just s

main = do
    print $ numUpper 3 2 "123AB"
    print $ numUpper 3 2 "123ABC"
    print $ numUpper 3 2 "12ABC"

실행 결과
Just "123AB"
Nothing
Nothing

Alternative



【문 6】문자열s의 선두 3문자가 「숫자+대문자+소문자」또는 「대문자+소문자+소문자」로 구성되는지를 판정하는 함수check s를 구현해 주세요. <|>를 사용하여 성공하면 Just s, 실패하면 Nothing를 반환합니다. 후속 문자는 판정에 영향을 주지 않습니다.
import Data.Char
import Control.Applicative
import Control.Monad

check s = do
    guard $ length s >= 3
    do
        guard $ isDigit $ s !! 0
        guard $ isUpper $ s !! 1
        <|> do
        guard $ isUpper $ s !! 0
        guard $ isLower $ s !! 1
    guard $ isLower $ s !! 2
    Just s

main = do
    print $ check "1"
    print $ check "2Ab"
    print $ check "Abc"
    print $ check "Ab1"
    print $ check "1AB"

실행 결과
Nothing
Just "2Ab"
Just "Abc"
Nothing
Nothing