IOモナドと末尾再帰の相性がいい

getLine で取り込んだ文字列をリストにして取り出したいと思って次のようにしたがうまくいかなかった。

Hugs> loop >>= return . (take 3) >>= print where loop = do {cs<-getLine; rest<- loop; return (cs:rest)}
hello
world
bye
bye
bye
bye
bye
bye
{Interrupted!}

IOモナドの世界では、遅延評価ができないので、loop が終わらないと次のtake 5が実行できないからだ。IOモナドで無限リストを利用する方法は使えない。

そこで、次のようにして末尾再帰を用いて、一回ごとにリストを更新するようにしたらうまくいった。末尾再帰は変数への代入ができない関数型プログラミングに、擬似代入を可能にする抜け道を提供してくれる。

Hugs> loop [] >>= print where loop x = do {cs <- getLine; if cs == "" then return x else loop (cs:x)}
hello
world

["world","hello"]

このように、IOモナドでループ処理を記述するときは、ちょっとした再帰関数の知識が必要だ。IOモナドは本質的には関数型のプログラムなので、手続き型のプログラムを関数型のプログラムに変換する知識が必要だからだ。これをクリアすればIOモナドのプログラムが怖くなくなる。


今日のHaskell

Hugs> let loop = do {getLine >>= print; loop ; return ()} in loop
hello
"hello"
world
"world"
 {Interrupted!}

IOモナドのループ処理の基本
[PR]
by tnomura9 | 2010-11-01 21:36 | Haskell | Comments(0)
<< Try Haskell IOモナドのまとめ >>