人気ブログランキング | 話題のタグを見る

Ruby版 askForWords

前々回のエントリーで、Yet Another Haskell Tutorial から askForWords という例を引いて、IOモナドのループは再帰関数で表現することを示したが、Rubyでも同じ関数がかけるのではないかという気がした。

そこで、やってみたが、あっさりと動いてしまった。次の例は irb で実行出来る。

irb(main):001:0> def askForWords
irb(main):002:1>   print "Please enter a word: "
irb(main):003:1>   word = gets.chomp
irb(main):004:1>   if word == ""
irb(main):005:2>     []
irb(main):006:2>   else
irb(main):007:2*     [word] + askForWords
irb(main):008:2>   end
irb(main):009:1> end
=> nil
irb(main):010:0> askForWords
Please enter a word: hello
Please enter a word: world
Please enter a word:
=> ["hello", "world"]

上の例を見ると、ループ処理を記述した Haskell の再帰関数が Haskell 特有のものではないことが分かる。手続き型のプログラムではあまり見かけないが、十分手続き型のプログラムとしても記述できるテクニックだったのだ。

そう考えると、IOモナドは特別に難しい Haskell だけの特徴だと特別視することはないのではないだろうか。ループを処理するのに再帰関数を使うという工夫がいるが、それもそれほど難しいテクニックでもないようだ。

また、上の関数を普通にループを使った記述をしてみると次のようになる。

irb(main):010:0> def askForWords
irb(main):011:1>   lst = []
irb(main):012:1>   print "Please enter a word: "
irb(main):013:1>   word = gets.chomp
irb(main):014:1>   while word != ""
irb(main):015:2>     lst = lst + [word]
irb(main):016:2>     print "Please enter a word: "
irb(main):017:2>     word = gets.chomp
irb(main):018:2>   end
irb(main):019:1>   return lst
irb(main):020:1> end
=> nil
irb(main):021:0> askForWords
Please enter a word: hello
Please enter a word: world
Please enter a word:
=> ["hello", "world"]

これをもとのプログラムと比べると、変数の初期化のために余分な記述が発生している。また、lst という余分な変数も使われている。記述が冗長になると、バグの発生の危険性も高くなる。また、戻値の記述は return lst となるので、どういう値が戻値になるのかを本体の処理を読んで推測しなくてはならないから、可読性にも問題があるのがわかる。

ループを再帰関数で記述する方法は、動作のオーバーヘッドやスタックのオーバーフローの問題がなければ、手続き型の言語でもよりコンパクトで可読性も高いプログラムが書けることが分かる。


今日のHaskell
Hugs> map (*2) [1..5]
[2,4,6,8,10]
Hugs> filter (<3) [1..5]
[1,2]
Hugs> foldr (+) 0 [1..5]
15

リスト操作は map と filter と foldr(l) が基本。
by tnomura9 | 2010-11-16 18:11 | Haskell | Comments(0)
<< 二分木探索 黄金比 >>