ふつける 4 章

ふつけるの 4 章。echo コマンド。

import System

main = do args <- getArgs
          putStrLn $ unwords args

import は open みたいなものか。getArgs はコマンドライン引数を得るアクション。これがアクションになっているのは面白い。OCaml で書くと

open Sys

let unwords = String.concat " "

let _ = print_endline (unwords (List.tl (Array.to_list argv)))

Sys.argv は C と同じく先頭に自分自身のコマンド名が入っているため、取り除く必要がある。また、なぜか list ではなく array なので String.concat を使うためには Array.to_list でリストにしなければならない。

次は fgrep コマンド。

import System
import List

main = do args <- getArgs
          cs <- getContents
          putStr $ fgrep (head args) cs

fgrep :: String -> String -> String
fgrep pattern cs = unlines $ filter match $ lines cs
  where
    match :: String -> Bool
    match line = any prefixp $ tails line

    prefixp :: String -> Bool
    prefixp line = pattern `isPrefixOf` line

ちょっと複雑。まず目につくのは where 節。これいいよなあ。OCaml にかなり欲しい。
fgrep 関数はまず入力を行で区切って、そしてマッチする行のみを取り出して文字列にする。where 節の中の match 関数は、まず tails 関数で n 文字の文字列に対して 1 文字目から n 文字目、2 文字目から n 文字目… n 文字目から n 文字目、そして空文字列を含むリストを作る。つまり、tails "hoge" は

[ "hoge",
   "oge",
    "ge",
     "e",
      ""
]

となるらしい。そして、pattern がそのいずれかのプレフィックスとなっていればマッチが成功したとみなし、True が返される。どのプレフィックスにもなっていなければ False となる。この match 関数を filter に渡せば pattern を含む行を抜き出すことができる。
filter 関数は OCaml の List.filter と同じ。List.tails は OCaml には無さそう。定義するとしたら

let tails ls =
  let f elt tls = (elt::(List.hd tls))::tls in
  List.fold_right f ls [[]]

ぐらいか。ううむ。なんか遅そう。
any は List.exists と一緒。List.isPrefixOf は ML には無さそう。だけど SML には String.isPrefix という関数があった。
関数を `` で囲むと二項演算子のように使えるらしい。どうしても必要な機能ということでもないと思うが面白い。OCaml にはこのような機能はないが、同じことをするトリッキーな方法を見付けた。 Simple idea for making a function infix によると
によると、

let ( /* ) x y = y x
and ( */ ) x y = x y 

と定義して、

"HOGE" /* String.contains */ 'O'

のように使える。すげえ。よくこんなの思い付くなあ。

よし、今日はここまでにしておこう。次は遅延評価か。HaskellOCaml の大きな違いのうちの一つだな。