正格性アナライザー

最近純粋関数な話題がご無沙汰に…。
このままでは駄目なので、今日はCleanの話を。
ここのページ、Haskellのことしか書いてないけど、
Cleanも似たような言語なので一時期勉強していました。
そのときはCleanのどうにもこうにもパフォーマンスハックな部分が
あまり好きになれなくて(というか、そのおかげで文法が
Haskellよりもずいぶん煩雑になっているような印象を受けました)
とりあえずはHaskellのほうをちゃんと勉強しようかなと
そういうような感じでいたのですが、
この前のパフォーマンス周りの件でCleanをちょっと見直してみました。
確か、Haskellのようなseqとか、データコンストラクタに対する正格性
だけではなくて、普通の関数の引数に任意に正格性フラグを付けられた
ような記憶があったので、ちょっと調べてみると
このような記述を見つけました。
なんと、Cleanはコンパイラは関数の引数の正格性を自動で決定できたらしい。
手動で書かなければならないものではなかったんですね…。
言われてみればもっともなことで、
そういうアルゴリズムが存在しても全然おかしくない。
正直言ってこれはすごいと思いました。


この機能を持ってすればこの前書いたHaskell版wcを
何も考えなくてもメモリを使わないプログラムに出来るのではないかと
思い、早速実装したのが以下のコード。

import StdEnv

Start world
  #(console,world) = stdio world
  #(nl,nw,nc) = wc console
  = toString nl +++ " " +++ toString nw +++ " " +++ toString nc +++ "\n"

wc f = inner f 0 0 0 True
where
  inner f nl nw nc prev
    #(b,c,f) = freadc f
    # nprev  = isSpace c
    # nnl    = nl + if (c=='\n') 1 0
    # nnw    = nw + if (prev&&not nprev) 1 0
    # nnc    = nc + 1
    | b = inner f nnl nnw nnc nprev
    | otherwise = (nl,nw,nc)

おおむね以前のHaskellコードと同じです。
コンパイルして実行した結果ですが、思惑通りメモリを消費せずに
計算できました。
Haskell版で慎重に慎重にseqをはさんでいたのが嘘のようです…。
実行時間は約5秒。これはC++でcinを使って一文字づつ処理した場合とほぼ同じ。


遅延評価処理系におけるパフォーマンス問題、
こういう方面の解決法もあったんですね。
いやはや、なんとも。