「ふつうのHaskellプログラミング」読みます(3)

4章やる。

4.1 コマンドライン引数の処理とモジュール

echo.hs
import System
main = do args <- getArgs
          putStrLn (unwords args)

SystemモジュールのgetArgsアクションを使ってる。これは

main = do args <- System.getArgs
          putStrLn (unwords args)

と、gtArgsがSystemモジュールに属してることを明示してもよい。

% ghc echo.hs -o echo
% ./echo hoge fuga moge
hoge fuga moge

System.getArgsアクションはコマンドライン引数を文字列のリストで渡す。それをunwordで連結してputStrLnで出力。

import System
main = do args <- System.getArgs
          putStr (unlines args)

unlinesでくっつけると以下のような結果に。

% ghc echo.hs -o echo
% ./echo hoge fuga moge
hoge
fuga
moge

ああ、unlinesは要素の末尾に改行追加するんだった。

Haskellプログラムはモジュールという単位に分割されてる。リファレンスマニュアルどこでダウンロードできるんだろうと探したら、既に「C:\ghc\ghc-6.8.2\doc」に展開されてた。Linuxとかならきっと/usr/share/doc以下のghcほげほげとかそんなディレクトリにあるんじゃないかなきっと。Debianだとghc6-docとかいうパッケージ名で入れれた。
モジュール一覧見てると、色々できそうに思えてくる。

Mainモジュール

main変数はMainモジュールに属する。Mainモジュールは一覧に見あたらん。mainしか無かったりするのかもしれない。

Preludeモジュール

基本的な色々なのある。今まで使った関数とかアクションなんかはここに属している。

4.2 総合演習

fgrep.hs
import System
main = do args <- getArgs
          cs <- getContents
          putStr (fgrep (head args) cs)

fgrep pattern cs = unlines (filter match (lines cs))
  where
    match line = any prefixp (tails line)

    prefixp line = pattern `isPrefixOf` line

こうか。

% ghc fgrep.hs -o fgrep

fgrep.hs:8:30: Not in scope: `tails'

fgrep.hs:10:27: Not in scope: `isPrefixOf'

Listをimportするの忘れてた。

import System
import List
main = do args <- getArgs
          cs <- getContents
          putStr (fgrep (head args) cs)

fgrep pattern cs = unlines (filter match (lines cs))
  where
    match line = any prefixp (tails line)

    prefixp line = pattern `isPrefixOf` line
% ./fgrep main <fgrep.hs
main = do args <- getArgs

おっけーです。where節はローカルな関数とか定義するのに使うようです。filter、anyはよくあるあれ(ひどい)。isPrefixOf a bはaがbの先頭に一致するときTrue、それ以外Falseを返す関数。tailsは[渡されたリストそのもの、第2要素以降、第3要素以降、……]というリストを返す関数。
`hoge`は演算子として定義せんでも中置記法として使えるようにする構文糖のようです。いいよ中置記法とか。

Prelude> import List
Prelude List> tails [1,2,3,4]
[[1,2,3,4],[2,3,4],[3,4],[4],[]]
Prelude List> isPrefixOf [1,2] [1,2,3,4]
True
Prelude List> isPrefixOf [] [1,2,3,4]
True
Prelude List> isPrefixOf [3] [1,2,3,4]
False

4.3 まとめ

MainモジュールとかPreludeモジュールとかあるらしいですよ。

遊ぶ

Preludeモジュールのドキュメント見ててcurryとuncurryなるもの見つけたんで、想像で遊ぶ。

Prelude> let plus3 n = n + 3
Prelude> let myMap = uncurry map
Prelude> myMap(plus3, [1,2,3])
[4,5,6]
Prelude> let plus3xs = curry myMap plus3
Prelude> plus3xs [1,2,3]
[4,5,6]

4.4 練習問題

sort.hs
main = do cs <- getContents
          putStr (unlines (sort (lines cs)))

だんだんLispっぽくて素敵な見た目になってきましたね。

% ghc sort.hs -o sort
% ./sort
cdef
cccc
abcd
acde
abcd
acde
cccc
cdef
group.hs

こんなんだろーと考えたら

import List
main = do cs <- getContents
          putStr (unlines (group (lines cs)))
% ghc group.hs -o group

group.hs:3:34:
    Couldn't match expected type `Char' against inferred type `String'
      Expected type: [Char]
      Inferred type: [String]
    In the first argument of `group', namely `(lines cs)'
    In the first argument of `unlines', namely `(group (lines cs))'

全然違った。groupってこんなん。

Prelude List> group [1,1,2,3,3]
[[1,1],[2],[3,3]]

じゃあこうですね。

main = do cs <- getContents
          putStr (unlines (map head (group (lines cs))))

いよいよますますLispじみてきて楽しいですね。おもわずputStrも括弧でかこむところでした。

% ghc group.hs -o group
% cat >test
hogehoge
hogehoge
fuga
moge
moge
% ./group <test
hogehoge
fuga
moge

おっけーです。

test