块局部变量用于防止块篡改其范围之外的变量。
使用块本地变量
x = 10
3.times do |y; x|
x = y
end
x # => 10
但这很容易通过声明常规块参数来完成。为该参数创建一个新的本地范围,该范围优先于先前的变量/范围。
不使用块局部变量
x = 10
3.times do |y, x|
x = y
end
x # => 10
在任何一种情况下,块外的变量x
都不会被更改。除了增强可读性之外,是否还需要块局部变量?
答案 0 :(得分:1)
块参数是一个真实参数,而一个块局部变量不是。
如果你给yield
这样的两个参数:
def foo
yield("hello", "world")
end
致电
x = 10
foo do |y; x|
puts x
end
x
在函数内部是nil
,因为只有第一个参数被赋给y
,第二个参数被丢弃。
调用
x = 10
foo do |y, x|
puts x
end
#=>world
x
将参数正确地设为"world"
。
答案 1 :(得分:0)
在Yu Hao's answer上展开,当调用只产生一个值的方法时,块参数和块局部的区别并不明显,但考虑一个产生多个值的方法:
def frob
yield 1, 2, 3
end
如果你传递一个带有单个参数的块,你会得到第一个值:
frob { |a| a.inspect }
# => "1"
但是如果你传递一个带有多个参数的块,你会得到多个值,即使你传递的参数太少或太多:
frob { |a, b, c| [a, b, c].inspect }
# => "[1, 2, 3]"
frob { |a, b| [a, b].inspect }
# => "[1, 2]"
frob { |a, b, c, d| [a, b, c, d].inspect }
# => "[1, 2, 3, nil]"
但是,如果您传递块范围的变量,则这些变量与产生的值无关:
frob { |a; b, c| [a, b, c].inspect }
# => "[1, nil, nil]"
类似的事情发生在产生数组的方法中,除了当你传递一个带有单个参数的块时,它会得到整个数组:
def frobble
yield [1, 2, 3]
end
frobble {|a| a.inspect }
# => "[1, 2, 3]"
然而,多个参数会破坏数组 --
frobble {|a, b| [a, b].inspect }
# => "[1, 2]"
-- 而块范围的变量没有:
frobble {|a; b| [a, b].inspect }
# => "[[1, 2, 3], nil]"
(即使存在块范围变量,多个值仍然会破坏数组:frobble {|a, b; c| [a, b, c].inspect }
会得到 "[1, 2, nil]"
。)
有关更多讨论和示例,另请参阅 this answer。