ブロック変数のスコープは「Rubyの落とし穴」だったのか!

追記 2008/12/16 18:18:41:

1.9では挙動が違う。こういう大事なところは…

>> x=[1,2,3]
=> [1, 2, 3]
>> x.map{|x|x}
=> [1, 2, 3]
>> x
=> [1, 2, 3]
>> proc{|x| x=1}.call(x)
=> 1
>> p x
[1, 2, 3]
ここまで


ライブドアブログ(livedoor Blog)| 読みたいブログが見つかるを読んで衝撃を受けた

まさか仕様だったとは…

#

ブロックの引数のスコープ

a = [1,2,3]
a.sort{|a,b| b<=>a} # a に代入される
p a.class #=> Fixnum # 最後に代入された a

なんてことだ。

>> x=[1,2,3]
=> [1, 2, 3]
>> x.map{|i|i}
=> [1, 2, 3]
>> x
=> [1, 2, 3]
>> x.map{|x|x}
=> [1, 2, 3]
>> x
=> 3

一方、ブロック内ので更にブロックを使った場合には汚染されない。

>> x=[[1,2],[3,4,5],[3]]
=> [[1, 2], [3, 4, 5], [3]]
>> x.map{|i|i.map{|i|i}}
=> [[1, 2], [3, 4, 5], [3]]
>> x.map{|i|i.map{|i|i+1}}
=> [[2, 3], [4, 5, 6], [4]]
>> x.map{|i|p i;i.map{|i|p i;i+1}}
[1, 2]
1
2
[3, 4, 5]
3
4
5
[3]
3
=> [[2, 3], [4, 5, 6], [4]]

どういうことだ?
いいの?これで?

schemeで言えば

>>(define x '(1 2 3))
=>x

>>(map (lambda (x) x) x)
=>(1 2 3)

>>x
=>(1 2 3)

こういう事だろ?おかしいよ。rubyのブロックって一体何だ?

んん?

>> proc{a=2}.call
=> 2
>> a=proc{|i| p i}
=> #<Proc:0xb7f6eb30@(irb):6>
>> a.call(1)
1

こうやってかんがえると common lispとおなじなら

(let ((x '(1 2 3)))
  (funcall (lambda (x) x) x)
  (print x))

(1 2 3) 

だから、procで書くと

>> x=[1,2,3]
=> [1, 2, 3]
>> proc{|x| x=1}.call(x)
=> 1
>> p x
1

こういう事かぁ lambda (x) のxが汚染されるんだな。

ちょっとまてよ commonlispならどうなるんだ?

(let ((x '(1 2 3)))
  (funcall (lambda (i) (print x)) x)
  (print x))

(1 2 3) 
(1 2 3)

lambdaの外はのぞけているなぁ。ブロック引数の汚染に何の意味があるんだろう?