型に嵌まる

Haskell初心者のお約束(「ふつける」読了程度)なんでしょうが,案の定「型に嵌まり」ました.やりたいことは

$> fib 10
55

のように,コマンドラインで引数で番号を与えると,対応するフィボナッチの項を返すというだけです.

悩んだ結果は以下の通り.

import Char
import System

fib :: Int -> Int
fib n 
    | n < 2 = n
    | otherwise = fib(n-1) + fib(n-2)

strToInt :: String -> Int
strToInt = foldl1 (\x y -> x * 10 + y ) . map digitToInt 

main = do strs <- getArgs
          putStr.show $ (fib.strToInt.head) strs

getArgsで取得されるのは「引数のリスト」なので,たとえ引数が一個でもそれは要素が一つのリストです.したがって,headで取り出さないと文字列にならないのです.そして,取り出した文字列が数字のみで構成されていても,それは文字列.数字ではないので,それを数字に直さないといけません.Perlならそこは勝手に処理してくれますけど,Haskellはしてくれません.

キャストの関数があるのかもしれないのですが,分からないので自前で変換するわけですが,どうも一桁の数字ならやってくれる関数digitToIntがあるとのこと.そこで取得した「数字からなら文字列」をmapでばらばらにして,それぞれの桁を変換して,「整数からなるリスト」を作ります."123"を[1,2,3]にするわけです.そのあと,(1*10+2)*10+3の計算をして123にするのですが,これはまさにfoldl1の仕事なので,それを使います.

ここまでやって,やっと引数の「数字の文字列」を整数に直して,それをフィボナッチの漸化式にいれて,結果の整数を表示させて,なんとか終了.
けど・・・やっぱり大きい番号では遅すぎます.再帰の回数が多すぎるのです.

コマンドラインで数字を受け取って,計算のタネにして,それをまた出力するという「ふつう」の作業のために苦労する・・・さすがHaskell,「簡単なことは難しく,難しいことは簡単」という評価は伊達じゃない(^^;;ただ,本質的な問題は「難しいことは簡単」というレベルに達してないということです.

コマンドラインの引数を解釈するパーサ(きっと既存のものはあるに違いないです。。PerlのGetOptみたいなもの)を考えるのも面白そうです.

追記(2007/07/09)

nobsunさん(あのnobsunさんですよね)からのコメントを頂いて

import Char
import System

fib :: Int -> Int
fib n 
    | n < 2 = n
    | otherwise = fib(n-1) + fib(n-2)

main =  putStr.show.fib.read.head =<< getArgs

となりました.かなりすっきりしました.ありがとうございました.こういう風にするんですね,モナドというのは.なるほど.それにしても,readは「ふつける」にもしっかり載ってました.はぅ〜.