Writerモナドを試してみた

30分プログラム、その670。Writerモナドを試してみました。
前回がReaderモナドだったので、今回はWriterモナドを試してみました。
The Writer monadには、Writerモナドは裏でログをとったりするのに便利だと書いてあったので、関数の呼び出しを記録してみました。

使い方

*Main> runWriter $ fib 10
(55,["fib(10)",
     "fib(9)",
     "fib(8)",
     "fib(7)",
     "fib(6)",
     "fib(5)",
     "fib(4)",
     "fib(3)",
     "fib(2)",
     "fib(2)",
     "fib(3)",
     "fib(2)",
     ....])

とやって、普通のフィボナッチ数の定義が、いかに非効率かに驚く。

そのあと、

*Main> runWriter $ fibi (1,0) 10
(55,["fibi(10)","fibi(9)","fibi(8)",...])

とやって、フィボナッチ数の反復的な定義が、いかに効率的かに驚く。

ソースコード

import Control.Monad.Writer
import Text.Printf

fib :: Int -> Writer [String] Int
fib 0 = return 0
fib 1 = return 1
fib n = do tell [printf "fib(%d)" n]
           a <- fib (n-1)
           b <- fib (n-2)
           return $ a + b

fibi :: (Int,Int) -> Int -> Writer [String] Int
fibi (current,_) 1 = return current
fibi (current,prev) n = do tell [printf "fibi(%d)" n ]
                           fibi (current+prev,current) (n-1)