Real World Haskell 4.6 練習問題

練習問題を解いてみた。

import Data.Char (digitToInt)
import Data.List 

-- 1.
-- 負の場合と正の場合で分かれてるのが気になる。
asInt_fold :: String -> Int
asInt_fold [] = 0
asInt_fold (x:xs)
  | x == '-'  = - (foldl' atoi 0 xs)
  | otherwise = foldl' atoi 0 (x:xs)
  where atoi a b = (a * 10) + (digitToInt b) 

-- 2.
asInt_fold2 :: String -> Int
asInt_fold2 [] = 0 
asInt_fold2 (x:xs)
  | x == '-'  = - (foldl' atoi 0 xs)
  | otherwise = foldl' atoi 0 (x:xs)
  where atoi a b = if myIsDigit b 
                   then (a * 10) + (digitToInt b) 
                   else error("invalid parameter")
        myIsDigit a = a `elem` "0123456789abcdfeABCDEF"

-- 3.
-- Either の使い方でとまどった。なんだよ、Right, Left って。
-- 練習問題はここまでに出てきたもので実装するものだとばかり思い込んでいたけど...
-- おかげで、myIsDigit なんて作っちゃったし。
type ErrorMessage = String
asInt_either :: String -> Either ErrorMessage Int
asInt_either [] = Right 0 
asInt_either (x:xs)
  | x == '-'  = let bad = (validate xs)
                in  if   null bad
                    then Right (-(foldl' atoi 0 xs))
                    else Left  (message bad)
  | otherwise = let bad = (validate (x:xs))
                in  if null bad
                    then Right (foldl' atoi 0 (x:xs))
                    else Left  (message bad)
  where validate xs = snd (span myIsDigit xs)
        myIsDigit a = a `elem` "0123456789abcdfeABCDEF"
        message a = "non-digit'" ++ (head a:'\'':[])
        atoi a b = (a * 10) + (digitToInt b) 

-- 4.
myConcat :: [[a]] -> [a]
myConcat xs = foldr (++) [] xs

-- 5.
myTakeWhileR :: (a -> Bool) -> [a] -> [a]
myTakeWhileR p (x:xs)
  | p x        = x : myTakeWhileR p xs
  | otherwise  = [] 
myTakeWhileR _ _ = []

myTakeWhileF :: (a -> Bool) -> [a] -> [a]
myTakeWhileF p xs = foldr step [] xs
  where step a b | p a       = a : b
                 | otherwise = []

-- 6.
-- 再帰で書いた方が簡単だった。
-- foldl' と foldr の違いを理解していなかったのが原因
myGroupBy :: (a -> a -> Bool) -> [a] -> [[a]]
myGroupBy p [] = []
myGroupBy p (x:xs) = foldl' step [[x]] xs
  where step ys a
          | p (head (last ys)) a = init ys ++ [(last ys)++[a]]
          | otherwise            = ys ++ [[a]]

-- 7.
-- 時間がなかったので、パッとできたものだけ。
-- cycle は畳み込みじゃ実装できなさそうだけど...。
-- リストの先頭からアクセスしなければいけない関数は foldl' を使った方がよさげ
-- any なんかは、順番は関係ないのでどっちでもいいのか?
myAny :: (a -> Bool) -> [a] -> Bool
myAny f xs = foldr step False xs
  where step x bool | f x       = True
                    | otherwise = bool