是否存在块局部变量只是为了增强可读性?

时间:2014-10-01 02:02:39

标签: ruby scope block

块局部变量用于防止块篡改其范围之外的变量。

使用块本地变量

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都不会被更改。除了增强可读性之外,是否还需要块局部变量?

2 个答案:

答案 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