練習問題

入門Haskell―はじめて学ぶ関数型言語」 p.78 より。

(1) filter,inits,tails,zip,zipWith を定義しなさい。

 myFilter f []     = []
 myFilter f (x:xs) | f x       = x:myFilter f xs
                   | otherwise = myFilter f xs
 
 myInits [] = []
 myInits xs = map ((flip take) xs) [1..(length xs)]
 
 myTails [] = []
 myTails xs = map ((flip drop) xs) [0..(length xs -1)]
 
 myZip _  [] = []
 myZip [] _  = []
 myZip (x:xs) (y:ys) = (x,y) : myZip xs ys
 
 myZipWith f _  [] = []
 myZipWith f [] _  = []
 myZipWith f (x:xs) (y:ys) = f x y : myZipWith f xs ys
 

結果。

 *Main> myFilter (> 0) [-2,-1,0,1,2]
 [1,2]
 *Main> myInits "abcde"
 ["a","ab","abc","abcd","abcde"]
 *Main> myInits ""
 []
 *Main> myTails "abcde"
 ["abcde","bcde","cde","de","e"]
 *Main> myTails ""
 []
 *Main> myZip [1,2,3] [1,2,3,4]
 [(1,1),(2,2),(3,3)]
 *Main> myZipWith (+) [1,2,3] [1,2,3,4]
 [2,4,6]

myZip,myZipWith の余った要素が無視されるのは正しい動作。

(2) sum,product,and,or のそれぞれを fold 系を使って定義しなさい。

 mySum :: (Num a) => [a] -> a
 mySum = foldl (+) 0
 
 myProduct :: (Num a) => [a] -> a
 myProduct = foldl (*) 1
 
 myAnd :: [Bool] -> Bool
 myAnd = foldl (&&) True
 
 myOr :: [Bool] -> Bool
 myOr = foldl (||) False

結果。

 *Main> mySum [1,2,3,4,5]
 15
 *Main> myProduct [1,2,3,4,5]
 120
 *Main> mySum []
 0
 *Main> myProduct []
 1
 *Main> myAnd [True, False, True]
 False
 *Main> myAnd [True, True, True]
 True
 *Main> myOr [False, False, True]
 True
 *Main> myOr [False, False, False]
 False

sum と product はそれぞれ 0,1 になる。

 *Main> sum []
 0
 *Main> product []
 1


追記:
IO () さんから myInits が無限リストに対応できていないと指摘をもらった。コメント欄参照。
ああっ,本当だ。

 *Main> take 10 $ map (take 1) $ myInits [1..]
 GHC's heap exhausted: current limit is 268435456 bytes;
 Use the `-M<size>' option to increase the total heap size.

ついでに気が付いたけど,inits の結果は先頭に空リストがあるじゃないか。この点でも違っている(これは「入門Haskell」も間違っている。p.75)。

 Prelude List> inits "abcde"
 ["","a","ab","abc","abcd","abcde"]

えーと,もしかして tails も?

 Prelude List> tails "abcde"
 ["abcde","bcde","cde","de","e",""]

……出直してきます。