例えば memoize しながらフィボナッチ数列を求める場合。
class Fibonacci
def initialize
@memo = [0, 1]
end
def fibonacci(n)
@memo[n] || @memo[n] = fibonacci(n - 2) + fibonacci(n - 1)
@memo[n] # この行は省略しても結果は同じ
end
end
fib = Fibonacci.new
10000.times { |n| fib.fibonacci(n) }
これで何が嬉しいのかというと、プロファイリングしてみるとこんな結果が現れます。
fibonacci メソッドの最後の行を省略しなかった場合
$ time ruby -rprofile fibonacci.rb
% cumulative self self total
time seconds seconds calls ms/call ms/call name
67.92 3.24 3.24 29996 0.11 0.21 Fibonacci#fibonacci
16.98 4.05 0.81 59992 0.01 0.01 Array#[]
6.29 4.35 0.30 1 300.00 4770.00 Integer#times
3.56 4.52 0.17 19996 0.01 0.01 Fixnum#-
2.73 4.65 0.13 9954 0.01 0.01 Bignum#+
2.52 4.77 0.12 9998 0.01 0.01 Array#[]=
0.00 4.77 0.00 2 0.00 0.00 Module#method_added
0.00 4.77 0.00 1 0.00 0.00 Fibonacci#initialize
0.00 4.77 0.00 45 0.00 0.00 Fixnum#+
0.00 4.77 0.00 1 0.00 0.00 Bignum#coerce
0.00 4.77 0.00 1 0.00 0.00 Class#inherited
0.00 4.77 0.00 1 0.00 0.00 Class#new
0.00 4.77 0.00 1 0.00 4770.00 #toplevel
real 0m6.005s
user 0m4.776s
sys 0m1.184s
fibonacci メソッドの最後の行を省略した場合
$ time ruby -rprofile fibonacci_fast.rb
% cumulative self self total
time seconds seconds calls ms/call ms/call name
66.39 2.39 2.39 29996 0.08 0.14 Fibonacci#fibonacci
8.06 2.68 0.29 29996 0.01 0.01 Array#[]
8.06 2.97 0.29 19996 0.01 0.01 Fixnum#-
6.94 3.22 0.25 1 250.00 3600.00 Integer#times
5.56 3.42 0.20 9998 0.02 0.02 Array#[]=
5.00 3.60 0.18 9954 0.02 0.02 Bignum#+
0.00 3.60 0.00 1 0.00 0.00 Fibonacci#initialize
0.00 3.60 0.00 1 0.00 0.00 Class#inherited
0.00 3.60 0.00 2 0.00 0.00 Module#method_added
0.00 3.60 0.00 1 0.00 0.00 Class#new
0.00 3.60 0.00 45 0.00 0.00 Fixnum#+
0.00 3.60 0.00 1 0.00 0.00 Bignum#coerce
0.00 3.60 0.00 1 0.00 3600.00 #toplevel
real 0m4.516s
user 0m3.600s
sys 0m0.904s
実行時間が約 25% 短縮されました。
注目するポイントは 2 行目の Array#[] メソッド、つまり配列から値を引き出す処理の回数が半分になっているというところ。どうしてこうなるかは、コードの意味を考えながら読めばすぐにわかると思います。ただ、こういう最適化によって、有意な効果を得られるケースがあるのかは疑問ですが・・・。
でもせっかくなので、代入の返り値はどうなってるのか、言語ごとに調べてみました。
Ruby
p (foo = "bar")
# => "bar"
JavaScript (Rhino)
var foo;
print(foo = "bar");
// => bar
Perl
my $foo;
print $foo = "bar";
# => bar
PHP
var_dump($foo = "bar");
// => string(3) "bar"
MySQL (これはちょっと番外編?)
mysql> SELECT @today := CURDATE();
+---------------------+
| @today := CURDATE() |
+---------------------+
| 2010-02-09 |
+---------------------+
1 row in set (0.00 sec)
と、ここまでは、どれも代入値が返ってくるようですが・・・
Python
print(foo = "bar")
# => SyntaxError: invalid syntax
Python では Syntax Error となります。
これを知って何の役に立つのかイマイチわかりませんが、こういうことになっていますよ、ということで。
Optimization, Ruby