プログラミングHaskell (第八章の練習問題)の前に

in

八章は原著で玉砕していて僕にとっては鬼門です。練習問題に入る前に原著を読んだときに躓いたことを整理しておくと、躓きはテキストの例題にある1番目と3番目をペアで返すpのコード

p = do x <- item'
       _ <- item'
       y <- item'
       return' (x,y)

を入力してもエラーになって先に進めなかったこと。このこと自体はググってみたら、

p = item Main.>>= \x ->
    item Main.>>= \_ ->
    item Main.>>= \y ->
    return' (x,y)

のように、(>>=)を使えばうまくコードを実行できることはわかったのですが、肝心の実装が理解できず苦しみました。例えば、`parse item "abcde"`の結果が('a',"bcde")であるなら、`p "abcde"`は、

return' (('a',"bcde"),('c',"de"))

じゃないの?というところから抜け出せずにい(た|る)わけです。

結果から考えれば"item >>= \x ->"のxは、関数itemの返す結果である"[(v,out)]"ではなくてvであることは明白なのですが、なぜそうなるのかの理由がうまく飲み込めなかったんです。

試行錯誤の中でようやく気づいたことは、先に書いた関数pの定義は、一行に書き直してみると

item >>= (\x -> item >>= (\_ -> item >>= (\y -> return (x,y))))

ということであり、複雑に見えた式も"item >>= (\x -> ...)"という構造であることが見えてきました。あらためて`p "abcde"`を実装にあてはめて考えてみると

case parse item "abcde" of
    [] -> []
    [('a',"bcde")] -> parse ((\x -> ...) 'a') out

となり、無名関数(\x -> ...)は引数v(='a')を取りますから、すなわち\x == 'a'ということになります。残りも同じ。

勉強をはじめたときは"pの結果にfを適用し..."でpの結果に即fを適用するように勘違いしてしまったのが混乱の始まりというように思います。演算子(>>=)の正体は「pの結果aを引数としParser bを返す関数」であり、pの結果aはあくまでもこの関数の引数であって、fに適用するための引数ではないことに気づかなかったんですね。

翻訳のこの部分を注意深く読んでみるとこうあります。

入力文字列に一番目のパーサーpを適用し、失敗すれば全体も失敗する。そうでなければ、関数fを一番目のパーサーの結果に適用して、二番目のパーサーを生成する。二番目のパーサーを一番目のパーサーの出力文字列に適用すると、最終的な結果が出る。

この「二番目のパーサーを生成する」というところが、英文読解力の無さと相まって理解できていなかったわけです。

実行して動作確認はとっていたものの、このように考えることが正しいのかどうか当初自信がなく、結果から類推したこじつけ論でごまかしているのではという恐怖もあったのですが、訳本がでて(P93、訳注5)なんとなく自信をもった次第。もちろん、ホントの理解はまだまだなんですが「遅延理解」の手法でなんとか前に進みたいと思っています。

この記事のトラックバックURL:

http://hippos-lab.com/blog/trackback/353

Comments