块
块是大括号 {}
(通常用于单行块)或 do..end
(用于多行块)之间的代码块。
5.times { puts "Hello world" } # recommended style for single line blocks
5.times do
print "Hello "
puts "world"
end # recommended style for multi-line blocks
5.times {
print "hello "
puts "world" } # does not throw an error but is not recommended
注意:大括号的优先级高于 do..end
生产
可以使用单词 yield
在方法和函数内部使用块:
def block_caller
puts "some code"
yield
puts "other code"
end
block_caller { puts "My own block" } # the block is passed as an argument to the method.
#some code
#My own block
#other code
但要小心,如果在没有阻挡的情况下调用 yield
,它会产生一个 LocalJumpError
。为此,ruby 提供了另一种名为 block_given?
的方法,它允许你在调用 yield 之前检查块是否已通过
def block_caller
puts "some code"
if block_given?
yield
else
puts "default"
end
puts "other code"
end
block_caller
# some code
# default
# other code
block_caller { puts "not defaulted"}
# some code
# not defaulted
# other code
yield
也可以为块提供参数
def yield_n(n)
p = yield n if block_given?
p || n
end
yield_n(12) {|n| n + 7 }
#=> 19
yield_n(4)
#=> 4
虽然这是一个简单的例子,但是 yield
ing 对于允许在另一个对象的上下文中直接访问实例变量或评估非常有用。例如:
class Application
def configuration
@configuration ||= Configuration.new
block_given? ? yield(@configuration) : @configuration
end
end
class Configuration; end
app = Application.new
app.configuration do |config|
puts config.class.name
end
# Configuration
#=> nil
app.configuration
#=> #<Configuration:0x2bf1d30>
正如你所看到的,以这种方式使用 yield
使得代码比不断调用 app.configuration.#method_name
更具可读性。相反,你可以执行块内的所有配置,保持代码包含。
变量
块的变量是块的本地(类似于函数的变量),它们在块执行时死亡。
my_variable = 8
3.times do |x|
my_variable = x
puts my_variable
end
puts my_variable
#=> 0
# 1
# 2
# 8
块无法保存,一旦执行就会死亡。为了节省块,你需要使用 procs
和 lambdas
。