LoginSignup
1
0

More than 5 years have passed since last update.

【解答例】Haskell Maybeモナド 超入門

Last updated at Posted at 2015-01-22

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

再実装

bind

【問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
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0