Haskell for Programming Contest

この夏休みにHaskellを勉強してみることにしました。
最近、趣味でプログラミングコンテストの問題を解いていますが、しばらくはHaskellで解いていきたいと思っています。
しばらく、ここにはHaskellの勉強の内容を書いていきます。よろしくお願いします。


HaskellはCなどの手続き型言語や、Javaなどのオブジェクト指向言語とは全く違う、純粋関数型プログラミング言語であるとのこと。

Haskellは基本的に型安全な言語なので、型が他の言語よりも厳密に検査されます。「Aがaの型である」を「A :: a」と表記します。また、関数Bがb0, b1, ..., bnの型のn個の引数を受け取ってcの型を返す場合、Bの型は「B :: b0 -> b1 -> ... -> bn -> c」となります。また型aのリスト(配列)の型は[a]となります。

とりあえず簡単なプログラムを組んでみようと思い、http://k2pc-easy.contest.atcoder.jp/tasks/k2pc001_e1を解いてみました。C++Javaで書くなら超簡単な問題です。


Haskellには他の言語のようにいくつかの基本的な型があり、Int、Char、Stringなどがあります。今回はIntとStringを使います。

まず、この問題を解くのに必要なのがIOです。入力をプログラムが読み込まなければなりません。

a b c
N

の入力が与えられたとき(0≦a≦1000、≦b≦2000、0≦c≦3000、0≦N≦1000)、これらをそれぞれ対応する整数としてプログラムに認識させます。以下の関数を組み合わせて使います。

  • getLine :: IO String ・・・ 入力を一行読み込む
  • words :: String -> [String] ・・・ 引数の文字列をスペース区切りで分割する
  • read :: String -> a ・・・ Stringの型の引数をaの型に変える

words getLine
とすれば入力「a b c」を「["a", "b", "c"]」のリストに出来そうですが、これはコンパイルエラーになります。wordsは引数の型がStringといっているのに、getLineは型がIO Stringであるためです

僕もまだよく理解していませんが、IOを使うと副作用が出る可能性があるため、getLineの戻り値の型はStringではいけないそうです。
よって、Monadを使ったり(参考 : http://www.shido.info/hs/haskell8.html)、fmapという関数を使って、IO String 型をStringに変換します(fmapは仕組みがよく分かってません・・・。関数適用という効果があるらしいです)。

これで、あとは0未満の整数を0に矯正する関数filを書き(関数、条件分岐の書き方などはhttp://www.shido.info/hs/haskell3.htmlが分かりやすい)、以下の関数を使えばこの問題のプログラムは完成です。

  • show :: a -> String ・・・ aの型の引数をString型に変える
  • putStrLn :: String -> IO () ・・・ 引数を出力する
fil n | n <= 0      = 0
		| otherwise = n
 
main = do
	abc <- fmap words getLine
	n <- getLine
	putStrLn $ show (  fil (read (n) - read ( abc !! 0)) ) ++ " " ++ show ( fil (2 * read (n) - read ( abc !! 1) ) ) ++ " " ++ show ( fil (3 * read (n) - read ( abc !! 2)) )

ちなみに「abc !! i」はリストのi番目の要素の取り出しです。
最後の行の$は便利な記法で、
a $ b $ c

a ( b( c ) )
を簡略化した記号です。すごく見やすい。

というわけで、以上が初めて書いたHaskellプログラムでした。所要時間は3時間くらい。

まだまだ書き溜めてあるプログラムがいくつかあるので、少しずつ紹介していきます。