Real World Haskell(3)[Exercise4-2]

foldlを使ってanyを定義すると....

import Data.List (foldl', groupBy)
import Data.Char (digitToInt, isDigit, intToDigit, isSpace)
import GHC.Base (maxInt)
import Data.Either

---Exercise 4.2.1
asInt_fold :: String -> Int
asInt_fold = foldl' helper 0
    where helper acc c = acc * 10 + digitToInt c

---Exercise 4.2.2
asInt_fold2 xs@(x:xr)
    | x == '-' = foldl' (helper (-)) 0 xr
    | otherwise = foldl' (helper (+)) 0 xs
    where helper fn acc c = fn (acc * 10) (digitToInt c)


---Exercise 4.2.3
intLength n = snd $ loop (n, 0) 
    where loop (0,c) = (0, c)
          loop (n,c) = loop (n `div` 10, c+1)

maxIntLength = intLength maxInt

asInt_fold3 "" = error "no input"
asInt_fold3 xs@(x:xr)
    | x == '-' = foldl' (helper (-)) 0 xr
    | length xs > maxIntLength = error "input value is too big"
    | otherwise = foldl' (helper (+)) 0 xs
    where 
      helper fn acc c 
          | isDigit c =  fn (acc * 10) (digitToInt c)
          | otherwise = error "input nodigit value" 


---Exercise 4.2.4
asInt_either xs 
    | xs == "" || xs == "-" = Left "no input"
asInt_either xs@(x:xr)
    | x == '-' = foldl' (helper (-)) (Right 0) xr
    | length xs > maxIntLength = Left $ "too big " ++ xs
    | otherwise = foldl' (helper (+)) (Right 0) xs
    where 
      helper _ (Left acc) _ = Left acc
      helper fn (Right acc) c 
          | isDigit c = Right $ fn (acc * 10) (digitToInt c)
          | otherwise = Left $ "non-digit '" ++ [c,'\'']

---Exercise 4.2.5(6)
concat1 :: [[a]] -> [a]
concat1 xss = foldr (++) [] xss

concat2 xss = reverse $ foldl' reverseAppend [] xss
    where reverseAppend = foldl' (flip (:))


---Exercise 4.2.7
takeWhile1 :: (a -> Bool) -> [a] -> [a]
takeWhile1 p (x:xr) 
    | p x = x : (takeWhile1 p xr)
    | otherwise = []
takeWhile1 _ _ = []

takeWhile2 p = foldr helper []
    where helper x ys = if p x then x : ys else []


---Exercise 4.2.8(9)
groupBy1 :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy1 p (x:xr) =  (x : item) : groupBy1 p rest
    where (item,rest) = span (p x) xr
groupBy1 _ _ = []

groupBy2 p (x:xr) = reverse $ foldl' helper [[x]] xr
    where helper (ys@(y:_):rest) x 
              | p x y =  (x : ys) : rest
              | otherwise = [x] : (reverse ys) : rest

---Exercise 4.2.10
--anyとかtakeを使わなそうなものはfoldlを使うとヤバい。

any1 :: (a -> Bool) -> [a] -> Bool
any1 p = foldr helper False
    where helper x ans = p x || ans

cycle1 :: [a] -> [a]
cycle1 [] = error "empty list"
cycle1 xs = foldr (:) (cycle1 xs) xs

words1 :: String -> [String]
words1 xs = applyDouble add $ foldr helper ([],[]) xs
    where applyDouble f (x,y) = f x y
          add [] rest = rest
          add tmp rest = (reverse tmp) : rest
          helper x (tmp,rest)
              | isSpace x =  ([], add tmp rest)
              | otherwise = (x : tmp, rest)
                                           
words2 xs = loop $ skipSpace xs
    where skipSpace = dropWhile isSpace
          loop [] = []
          loop xs = item : (loop $ skipSpace rest)
              where (item,rest) = break isSpace xs

-- take 3 $ words $ cycle "abc " -- => ["abc","abc","abc"]
-- take 3 $ words1 $ cycle "abc " -- => *** Exception: stack overflow
-- take 3 $ words2 $ cycle "abc " -- => ["abc","abc","abc"]

--listを順にたどるなら、foldで大丈夫
--これをxs@(x:xr)に分割していると考えれば
--分割部分も関数としてくくり出したhyperFoldrとか創れるかも?

hyperFoldr _ _ init [] = init
hyperFoldr step split init seq = 
    step item $ hyperFoldr step split init rest
        where (item,rest) = split seq

words3 xs = hyperFoldr (:) split [] $ skipSpace xs
    where skipSpace = dropWhile isSpace
          split seq = (item, skipSpace rest)
              where (item,rest) = break isSpace seq
--単にnullの時の処理が消えているだけなのであまり意味がないような気もする。
-- take 3 $ words3 $ cycle "abc " -- => ["abc","abc","abc"]

unlines1 :: [String] -> String
unlines1 = foldr helper [] --とても高コスト(たぶん)
    where helper x ys = x ++ "\n" ++ ys

unlines2 xs = concat $ reverse $ foldl' helper [] xs
    where helper acc x = "\n" : x : acc