久しぶりにrubyで遊ぶ

rubyを始めた時に、くくを出力するプログラムを作ったりしました。
くくはこんな感じで書けると思います。
(9*9じゃないからnnかもしれません><)

def kuku n
  (1..n).map{ |x| (1..n).map{ |y| x * y}}
end
kuku(2) # => [[1, 2], [2, 4]]

これはn*nの要素で成り立ってます。
それなら、n*n*nの要素で成り立つくく的なものも定義できるはずです。

def kukuku n
  (1..n).map{ |x| (1..n).map{ |y| (1..n).map{ |z| x * y * z}}}
end
kukuku(2) # => [[[1, 2], [2, 4]], [[2, 4], [4, 8]]]

mapが3つになってしまいました><。
4,5,6...と増やしていった場合のくく的なものはどうなるんでしょう。
mapをたくさん書くのは面倒ですね。*1
一般的な形で定義したいところです。
というわけで少し考えてみました。出来上がったのが以下のようなものです。

def ku_n1 n, size, re=(1..n)
  (1..size-1).inject(re) do |r,_|
    r.map do |x|
      if x.respond_to? :map 
        ku_n1(n,size-1,x )
      else
        (1..n).map{ |y| x * y }
      end
    end
  end
end

少し読みにくいかもとか思いました。
procで2つに分けると読みやすいかもしれません。

def ku_n2 n,size, re=(1..n)
  f = proc do |x| 
    if x.respond_to?(:map)
      ku_n2(n,size-1,x)
    else 
      (1..n).map{ |y| x * y}
    end
  end
  (1..size-1).inject(re){ |r,_| r.map{|x| f.call(x)}}
end

速度の方はどうなのでしょうか?
前に実験したときの感覚からよると、ku_n2の方がとてもおそい気がします。
速度を計ってみましょう。

require 'benchmark'
Benchmark.bmbm do |x|
  1.upto(2){ |n| x.report("ku_n#{n}"){ send("ku_n#{n}", 10, 5)}}
end

1.8の結果

(ruby 1.8.6 (2007-06-07 patchlevel 36) [i486-linux])
Rehearsal -----------------------------------------
ku_n1   1.530000   0.170000   1.700000 (  1.695043)
ku_n2   4.820000   0.290000   5.110000 (  5.115461)
-------------------------------- total: 6.810000sec

            user     system      total        real
ku_n1   1.580000   0.140000   1.720000 (  1.736124)
ku_n2   4.860000   0.260000   5.120000 (  5.127700)

記憶が確かなら、block内で関数を読んでいる時に重くなるような感じです。
(1.9だとどうなっているかは分かりません><)

1.9でも計ってみました

(ruby 1.9.0 (2008-03-01 revision 15664) [i686-linux])
Rehearsal -----------------------------------------
ku_n1   0.560000   0.000000   0.560000 (  0.563815)
ku_n2   1.010000   0.000000   1.010000 (  1.007135)
-------------------------------- total: 1.570000sec

            user     system      total        real
ku_n1   0.550000   0.000000   0.550000 (  0.545552)
ku_n2   0.950000   0.000000   0.950000 (  0.954810)

1.8の時は3倍くらい差があったのですが、1.9では2倍くらいになっています。
というか、1.9は速いですね。とても速いです。

*1:evalを使って作るというのも何だか邪道な感じです><