『入門Haskell』練習問題例解(5)

takeとdrop

ではp.72の(1)から。

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

これは仕様通りに

myTake :: Int -> [a] -> [a]
myTake _ []         = []
myTake n _ | n <= 0 = []
myTake n (x:xs)     = x : myTake (n-1) xs

myDrop :: Int -> [a] -> [a]
myDrop _ []          = []
myDrop n xs | n <= 0 = xs
myDrop n (_:xs)      = myDrop (n-1) xs

でいいでしょう。

splitAt

p.72の(2)。

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はtakeとdropを用いて

splitAt :: Int -> [a] -> ([a],[a])
splitAt n xs = (take n xs, drop n xs)

と定義されるのですが、ここでは逆にsplitAtからtakeとdropを定義しなさい、と言っています。

mySplitAt :: Int -> [a] -> ([a], [a])
mySplitAt n xs = mySplitTuple n ([], xs) where
	mySplitTuple _ (x, [])    = (x, [])
	mySplitTuple n t | n <= 0 = t
	mySplitTuple n (x, y:ys)  = mySplitTuple (n-1) (x ++ [y], ys)

myTake :: Int -> [a] -> [a]
myTake n xs = fst (mySplitAt n xs)

myDrop :: Int -> [a] -> [a]
myDrop n xs = snd (mySplitAt n xs)

こうなると思います。ここで使った mySplitTuple n (xs, ys) は「ysの頭からn要素取り除いてxsの尾に連結する」という関数です。

takeWhile

p.72の(3)です。

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

(2)よりは簡単ですね。

myTakeWhile :: (a -> Bool) -> [a] -> [a]
myTakeWhile f [] = []
myTakeWhile f (x:xs)
  | f x       = x : myTakeWhile f xs
  | otherwise = []

仕様書にも同じように書いてあります。