合成数列の和 Advent Calendarに、Haskellで参加してみました。
Haskellを選んだ経緯
少し前の日にC++版を上げましたが、いっときはテンプレートメタプログラミングを考えていました。ただ、書こうとしてのあまりのやりづらさに、結局「これで苦しむぐらいなら、同じく純粋関数型のHaskellで書いたほうがいい」という考えに至り、Haskellで書いてみることとしました。
とはいえ、Haskellでコードを書くのは初めてなので、すごいH本を片手に進めていくこととしました。
まずはコード
dividors :: [Int]
dividors = [2] ++ iterate (+2) 3
target :: Int -> [Int]
target x = takeWhile (\k -> k * k <= x) dividors
isComposite :: Int -> Bool
isComposite x = any (\k -> x `mod` k == 0) (target x)
composites :: [Int]
composites = [x | x <- [4, 5..], isComposite x]
compositeSum :: Int -> Int
compositeSum cnt = sum $ take cnt composites
main = do
num <- readLn :: IO Int
putStrLn $ show $ compositeSum num
解説
無限リスト
Haskellは遅延評価なので、無限個のリストも扱うことができます。iterate (+2) 3
で3から始まる無限の奇数列を作って、先頭に2を追加して試し割りするリストを作っておきます(素数列を作ればいいのですが、ぱっとそこまでたどり着くことはできませんでした)。
そして、target
で必要な分(平方根まで)を切り出しています。
リスト内包表記
[x | x <- [4, 5..], isComposite x]
という短い式ですが、これだけで「4以上の整数から合成数だけ抜き出す」という意味になります。そして、この結果も無限リストのままなので、いくつでも必要なだけ抽出できます。
do
記法
Haskellは純粋関数型言語である以上、そのまま入出力を扱うことはできません。モナドという仕組みを介しているのですが、do
記法はそれを命令形言語のような見た目で書くことを可能としています。
おわりに
Haskellは初心者なので、改善点などありましたらコメントをいただければと思います。