takeWhileでtake、dropWhileでdropを定義する

ちょっと前に id:uskz 先生に「PStade.OvenはtakenWhileからtakenを定義してるから、Haskellでもできるんじゃないですか」とネタ振りしてたので、書いてみました。


インデックスのリストと一緒にループして、インデックスと比較する述語をxxxWhileに渡します。
インデックスリストは結果としていらないので、最後にインデックスを除いたリストを抽出。


take。

take2 :: Int -> [a] -> [a]
take2 n xs = map snd (takeWhile (preds n) (zxs xs))
  where
    zxs xs = zip (enumFrom 0) xs
    preds n zx = (fst zx) < n
 
main = print $ take2 5 [3..10]
[3,4,5,6,7]


drop。

drop2 :: Int -> [b] -> [b]
drop2 n xs = map snd (dropWhile (preds n) (zxs xs))
  where
    zxs xs = zip (enumFrom 0) xs
    preds n zx = (fst zx) < n
 
main = print $ drop2 5 [3..10]

ちなみに、これを作る過程で、関数の型を以下のように書いたらコンパイルエラーになりました:

take2 :: a -> [b] -> [b]
prog.hs:2:33:
    Could not deduce (Ord a) from the context ()
      arising from a use of `preds' at prog.hs:2:33-39
    Possible fix:
      add (Ord a) to the context of the type signature for `take2'
    In the first argument of `takeWhile', namely `(preds n)'
    In the second argument of `map', namely
        `(takeWhile (preds n) (zxs xs))'
    In the expression: map snd (takeWhile (preds n) (zxs xs))

prog.hs:2:43:
    Could not deduce (Enum a, Num a) from the context ()
      arising from a use of `zxs' at prog.hs:2:43-48
    Possible fix:
      add (Enum a, Num a) to the context of
        the type signature for `take2'
    In the second argument of `takeWhile', namely `(zxs xs)'
    In the second argument of `map', namely
        `(takeWhile (preds n) (zxs xs))'
    In the expression: map snd (takeWhile (preds n) (zxs xs))

元々のtakeの定義に合わせて第1引数の型を多相型ではなくIntにしたらコンパイルが通りました。