ファイルの行数を数える
countline.hs (p.39)
短かいので do 式は使わずに、
main = getContents >>= \x -> print $ length $ lines x
関数合成を利用すると、(cf. Haskell の関数合成)
main = getContents >>= print . length . lines
ファイルの先頭から指定した行数だけ表示
head.hs (p.45)
こちらも do 式を使わず、
main = getContents >>= putStrLn . getlines 10 getlines n = unlines . take n . lines
ところで、これは次のようにも書ける。
main = getContents >>= printLines 10 printLines n = putStrLn . unlines . take n . lines
一体どちらのコードがいいのかな?
Introduction to QuickCheck – HaskellWiki によると、
… the side effecting monadic code is mixed in with the pure computation, making it difficult to test without moving entirely into a "black box" IO-based testing model.
先ほどの関数の型を比較すると、
*Main> :t getlines getlines :: Int -> String -> String *Main> :t printLines printLines :: Int -> String -> IO ()
前者のの方が IO モナドを含まず、純粋な関数なのでテストしやすくて良いようだ。
ファイルの末尾から指定した行数を表示
tail.hs (p.51)
head.hs と同じようにして、
main = getContents >>= putStrLn . tailLines 3 tailLines n = unlines . reverse . take n . reverse . lines
しかし、tailLines 関数を見ると、関数 lines – unlines, reverse – reverse が対になっており、真ん中に take 関数が置かれているように見える。関数を関数で挟みこむデコレータのような関数を定義してシンプルにできないものか…。(cf. Python のデコレータ式 (1))
次のように関数 f を 関数 x, y で挟む関数を次のように定義してみる。
(***) x y f z = y $ f $ x z
これは関数合成を使った方が見やすいかも。
(***) x y f = y . f . x
これにより対になる関数を定義。
l = lines *** unlines r = reverse *** reverse
上記の関数を試してみる。
*Main> l (r $ take 2) "hoge\npiyo\nfuga\n" "piyo\nfuga\n"
動作したので takeLines を書きなおすと、
tailLines n = l $ r $ take n
関数合成を使うなら、
tailLines = l . r . take
全体は、
main = getContents >>= putStrLn . tailLines 3 (***) x y f = y . f . x l = lines *** unlines r = reverse *** reverse tailLines = l . r . take
うーん、返ってわかりずらくなったような気が… (@_@;) QQQ
0コメント:
コメントを投稿