块是大括号 {}(通常用于单行块)或 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

虽然这是一个简单的例子,但是 yielding 对于允许在另一个对象的上下文中直接访问实例变量或评估非常有用。例如:

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

块无法保存,一旦执行就会死亡。为了节省块,你需要使用 procslambdas