Ad Network

あわせて読みたい

  • あわせて読みたい

« Inventory Turnover(在庫回転率)とは | Main | Tech-on! にUIEngine搭載のカーナビの記事が... »

教えながら学ぶRuby: それでも僕は君をイテレータと呼ぶ

 前回の「イテレータに片思い」、たくさんのフィードバックをいただき、とても良い勉強になっている。一人でこつこつと勉強するよりも、励みになるしずっと楽しい。

 ちなみに、1問目の問題だが、

def hello(to, *mesg)  print "Hello, ", to, ".\n"

 print "-- message -- \n"

 for m in mesg

  print m, "\n"

 end

end

これに関しては、予想した通り、ほとんどの人が私と同じく、

def hello(to, *mesg)  print "Hello, ", to, ".\n"

 print "-- message -- \n"

 mesg.each { |m| print m, "\n" }

end

という答えにたどりついた({..} のかわりに do..end でも良い)。圧倒的に読みやすいし、とにかく美しい。「プログラムはできるだけ読みやすくあるべき」というポリシーの私としては、これだけで「Rubyに一目惚れ」である。生みの親のまつもとゆきひろ氏の「読みやすいコード」への思いが伝わってくる。

 ところが、2問目の問題、

def fact(n)  return 1 if n==0

 f=1

 while n>0

  f*=n

  n -= 1

 end

 return f

end

に関しては、私の用意していた答えは、

def fact(n)

 f=1

 (1..n).each { |i| f*=i }

 return f

end

なのだが、他にも色々な書き方があるようで、

def fact(n)

 f=1

 n.downto 1 do

  |i| f*=i

 end

 return f

end

などのバラエティも寄せられ、「同じことが色々な方法ができるというのはあまり美しくないな」ともう二回目のデートから相手のあらが見えてしまうしまつ。そんなときに、

def fact(n)

 (1..n).inject(1) {|r, i| r*i}

end

という私がまだ学んでいない inject なるもので f=1 を排除したものが寄せられ、「うむむ」と思わせるあたりが男心をくすぐるというか何と言うか。

 ちなみに、最近はこのイテレータのことはイテレータとは呼ばずにブロックなどと呼ぶらしいが、私としては生みの親の気持ちを尊重してイテレータと呼び続けたい。

1回しか呼ばれなかろうと  時には一度も呼ばれなかろうと   それでも君はイテレータ

TrackBack

TrackBack URL for this entry:
http://www.typepad.com/services/trackback/6a00d8341c4f9853ef00e54f050c348834

Listed below are links to weblogs that reference 教えながら学ぶRuby: それでも僕は君をイテレータと呼ぶ:

Comments

kazutanaka

>1回しか呼ばれなかろうと
>時には一度も呼ばれなかろうと
>それでも君はイテレータ

まったくイテレーションしないバージョンもどうぞ。

def fact(n)
proc{|f|g=proc{|q|proc{|m|f[q[q]][m]}};g[g]}[proc{|h|proc{|n|n<2?1:n*h[n-1]}}][n]
end

これで片思いの気持ちもさめる?

Andy

自分もいまRubyの勉強を始めたところです。
質問なのですが,以下のような問題はイテレータだけで解けるでしょうか。
配列Aと配列Bがあり,配列Aには各項目に文字列が,配列Bには配列Aの項目の一部が入っています。
このとき,配列Aにおける順序と配列Bにおける順序は一致しています(配列Aの小さいインデックスに入っている方が,配列Bでも小さいインデックスになる)。配列Aにおいてはいくつか同じ文字列の項目があるので,マッチングを一意に決めるにはこの条件が必要です(また,この条件からハッシュは利用できません)。
この条件で配列Aのどの項目と配列Bの項目がマッチするかを調べる方法が問題です。

例 
A…りんご,ばなな,みかん,りんご,かき,ぶどう
B…ばなな,りんご,ぶどう
とすると2番目,4番目,6番目にマッチする

旧来型プログラミング的考え方だとどこまで探したかインデックスをどこかの変数に入れてやっていくと思いますが,あまりきれいではないですよね。もしこれがイテレータできれいに解けるのなら感動すると思います。

Andy

ちなみに自分で解いたのは以下の方法。イテレータは使ってますが,「だからどうよ」って感じです。

a = Array["りんご","ばなな","みかん","りんご","かき","ぶどう"]
b = Array["ばなな","りんご","ぶどう"]

results = Array.new # 結果格納用配列
i = 0 # a 配列の中の調べているインデックス
max = a.size

b.each do |fruit|
(i..max-1).each do |j|
if a[j] == fruit
results[j] = 1
i = j
break
end
end
end

# 結果チェック
(0..max-1).each do |i|
if results[i] == 1
puts a[i]
end
end

kazutanaka

>イテレータだけで解けるでしょうか。

共通要素で重複除去するならアンドで一発。
 puts a & b

重複除去しないならイテレータで。
 puts a.select{|x| b.include?(x)}

あまりRubyっぽくなかったので直してみた。

プレビューしたらアンドのフォントが
変だったけどなんでだろう。

nido

同じ処理を色々な書き方ができるのがRubyの特徴の一つなので、そこは慣れて頂かないと・・。

ちなみに1問目の
 mesg.each { |m| print m, "\n" }
は自分なら
 puts mesg.join("\n")
かなぁ。

nido

>> Andyさん
こんなんでどうでしょ?

a = Array["りんご","ばなな","みかん","りんご","かき","ぶどう"]
b = Array["ばなな","りんご","ぶどう"]

results=[]
b.inject(0){|i,e| i+=a[i..-1].index(e);results[i]=1;i}
# 結果チェック
a.each_with_index {|e,i| puts e if results[i]==1}

Andy

kazutanakaさん,nidoさん,ありがとうございました。

『ばぶる』

Rubyの生みの親の名前は「まつもとゆきひろ」さんです...

satoshi

>Rubyの生みの親の名前は「まつもとゆきひろ」さんです...

ご指摘ありがとうございます。修正しました。

田辺

美しい詩歌が初学者に対しては不明瞭であったりするので
読みやすさ(理解のしやすさ)と美しさはイコールではない、と考えます。

また、可読性(読みやすさ)と記述性(書きやすさ)とは区別した方がよいと思います。
each メソッドはどちらかというと記述性を向上させています。
たとえば、Ruby を知らないけど英語を知っている人は
下の二つのソースのうち、上の方を読みやすく感じるでしょう。
ずるして、for を each に書き換えてますが、下の方は
ループ変数を | | で囲むとこが自然言語からすると特殊です。

each m in mesg

  print m, "\n"

end


mesg.each {

  |m| print m, "\n"

}

個人的には、puts mesg.join("\n") は
上のものよりは読みにくいけど美しいかと。

Post a comment

This weblog only allows comments from registered users. To comment, please Sign In.