練習問題

今日は目先を変えて練習問題をやろう。
入門Haskell―はじめて学ぶ関数型言語」 p.72 より。

①前ページの実装から,takeとdropに大きな値や負の値が入った場合の対処をしなさい。

前ページの実装とはこれ。Prelude の関数とかぶってはいけないので名前は変えてある。

 mytake 0 _      = []
 mytake n (x:xs) = x : mytake (n-1) xs
 
 mydrop 0 xs     = xs
 mydrop n (x:xs) = mydrop (n-1) xs

mytake に大きな値を入れるとリストそのもの,負の値を入れると空リストが返るようにする。逆に mydrop に大きな値を入れると空リスト,負の値を入れるとリストそのものが返るようにする。

 mytake _ []     = []
 mytake 0 _      = []
 mytake n (x:xs) | n < 0     = []
                 | otherwise = x : mytake (n-1) xs
 
 mydrop _ []     = []
 mydrop 0 xs     = xs
 mydrop n (x:xs) | n < 0     = x:xs
                 | otherwise = mydrop (n-1) xs

もっとすっきり書けそうだけどまぁいいか。実行結果。

 *Main> mytake 10 [0,1,2,3,4,5]
 [0,1,2,3,4,5]
 *Main> mytake (-2) [0,1,2,3,4,5]
 []
 *Main> mydrop 10 [0,1,2,3,4,5]
 []
 *Main> mydrop (-2) [0,1,2,3,4,5]
 [0,1,2,3,4,5]


ふたつめ。

②take と drop を同時に実行する splitAt :: Int -> [a] -> ([a], [a]) があります。たとえば
splitAt 2 [1,2,3,4] -- ([1,2], [3,4])
のように,結果のタプルの第1要素が take ,第2要素が drop になります。この splitAt を定義しなさい。また,take と drop を splitAt を使って定義し直しなさい。

まずは splitAt のほうから(名前は変えてある)。

 mysplitAt _ [] = ([], [])
 mysplitAt n xs = ((take n xs), (drop n xs))

結果。

 *Main> mysplitAt 2 [1,2,3,4]
 ([1,2],[3,4])

よし。じゃ,これを使って mytake と mydrop を定義し直すと

 mytake n xs = fst $ mysplitAt n xs
 
 mydrop n xs = snd $ mysplitAt n xs

結果は

 *Main> mytake 3 [1,2,3,4,5]
 [1,2,3]
 *Main> mydrop 3 [1,2,3,4,5]
 [4,5]


もうひとつ。

③takeWhile は,(a -> Bool) -> [a] -> [a] という型です。takeと似ていますが,決まった数だけとるのではなく,要素を計算し たら真である限りtakeし,一度でも失敗したら残りは返しません。これを定義しなさい。

こんなんでどうかな。

 mytakeWhile _ [] = []
 mytakeWhile f (x:xs) | f x       = x : mytakeWhile f xs
                      | otherwise = []
 *Main> mytakeWhile (\x -> x > 0) [3,2,1,0,-1,-2,-3]
 [3,2,1]
 *Main> mytakeWhile (\x -> x < 0) [3,2,1,0,-1,-2,-3]
 []