1、什么是代码块

        在Ruby中,{}或do...end之间的代码是一个代码块。代码块只能出现在一个方法的后边,它紧接在方法最后一个参数的同一行上,由yield关键字调用。例如:

[1,2,3,4,5].each { |i| puts i }

[1,2,3,4,5].each do |i| 
puts i
end

块变量:以yield关键字调用block也可以传递参数,block中竖线(|)之间给出的参数名用于接收来自yield的参数。

             竖线之间(如上例中的 | i |)的变量被称作块变量,作用和一个正常方法的参数一样

 

2、{}和do...end优先级不同

       在传递一个block时,使用{}传递的block比使用do…end的优先级要高;

为了避免引起歧义,最好使用()将参数括起来。例如:

1.upto 3 do |x|
  puts x
end

是正确的,但是 1.upto 3 {|x| puts x} 编译不通过,应该写成   1.upto(3) {|x| puts x}

 原因:

1.upto 3 do…end 中block会传递到upto方法里面,3会作为一个参数传递给upto

1.upto 3 {|x| puts x} 一句会把3当做函数名,将block传递到这个函数,其返回值作为upto方法的参数,所以编译不过,需加()。

 

3、代码块转化为对象的方法

代码块并不是对象,但可以通过以下三种方法转化为Proc类的对象:

(1)将一个代码块传递给最后一个参数以 &开始的方法。例:

def meth1(p1, p2, &block)
  puts block.inspect
  puts block.call
end
meth1(1, 2) { "This is a block" }

(2)使用Proc.new方法,代码块作为参数:

block = Proc.new { "a block" }

(3)调用Kernel.lambda方法:

block = lambda { "a block" }

    前两种方法是等价的,会检查参数的数量,但Proc.new 创建的对象不会;

用 lambda 和 Proc.new 生成的 Proc 对象之间也是有差别的。这个差别与 return 关键字相关。lambda 中的 return 从 lambda 返回。而 Proc.new 中的 return 从外围方法返回。

# 执行后"Never come here"不会被输出,执行p.call相当于在test_proc方法内执行了return语句。
def test_proc
  p = Proc.new { return 1 } # test_proc方法返回
  p.call 
  puts "Never come here" # 永远不会执行到
end

# 使用lambda生成的Proc对象执行call方法调用时,return表示从lambda包围得块内返回。
def test_lambda
  p = lambda { return 1 }
  result = p.call
  puts "The value is: #{result}"
end

Note:在一个代码块中执行next语句会导致代码块返回。返回值就是next语句后带的参数。如果next后没有参数,那么返回值为nil。

def meth2
  result = yield
  p "The block result is #{result}"
end

puts meth2 { next 9 } 
pr = Proc.new { next 100 }
puts pr.call
pr = lambda { next }
puts pr.call

执行结果为:
The block result is 9
100
nil

4、yield关键字调用代码块

在方法中可以使用 yield 来执行代码块的内容,就好像传入的代码块是这个方法的一部分一样。每当碰到一个 yield,代码块的内容就会被执行一次,代码块执行结束后,程序会回到 yield 的那一行继续向下执行。
使用 yield 可以向代码块传递参数,也可以从代码块取回返回值,返回值就是代码块中最后一个表达式的值。
def fibonacii(max)
  f1, f2 = 1, 1
  while f1 <= max
    yield f1
    f1, f2 = f2, f1+f2
  end
end

fibonacii(1000) { |f| print f, " " }

执行结果为:
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987

(1)在这个例子中,yield 接收一个参数f1,这个参数将会在执行的时候传递给指定的代码块。在代码块中,接收的参数使用两个竖线括起来放在代码块的头部。

(2)另外,在使用 yield 关键字调用代码块时,如果传入的参数个数少于代码块中定义的参数个数,那么没有传递的参数会自动转为nil。反之,则最后一个参数为一个数组,该数组包含了剩余的传递参数;