在ruby-koans中接触到yield和block的使用,刚接触有点疑惑,也有兴趣往下挖掘看看yield block的神奇所在。
1. yield占位。首先截取ruby-koans中关于yield和block的用法的实例。
def method_with_block
result = yield
result
end
def test_methods_can_take_blocks
yielded_result = method_with_block { 1 + 2 }
assert_equal 3, yielded_result
end
def test_blocks_can_be_defined_with_do_end_too
yielded_result = method_with_block do 1 + 2 end
assert_equal 3, yielded_result
end
在这段代码中,我们可以看到在第一个函数通过引入yield定义了method with block 方法, 而在第2个方法中则调用函数 method_with_block, 按照ruby中对方法调用的用法,这里我们的 { 1 + 2 } 和方法3中的 do 1 + 2 end 都是作为参数传递给method_with_block方法。只不过这里传递的参数不是我们常见的object,这里传递的是一个block代码块。
既然是代码块,那我们的方法中应该怎么去执行去使用这段代码呢。此时yield出现了,他直接告诉代码块,快到我这里来吧,我这有个坑,专门留给你的。于是这个被传进来的代码块毅然的在yield所在的地方扎根。
扎根后代码就做了以下事情,用伪代码表示如下:
def method_with_block
result = { 1 + 2 }
result
end
yielded_result = method_with_block
终于method_with_block执行代码块后将结果3返回出来最后被赋值给yielded_result. 于是yield的使命完成了。
2. yield赋值
# ------------------------------------------------------------------
def method_with_block_arguments
yield("Jim")
end
def test_blocks_can_take_arguments
method_with_block_arguments do |argument|
assert_equal "Jim", argument
end
end
# ------------------------------------------------------------------
def many_yields
yield(:peanut)
yield(:butter)
yield(:and)
yield(:jelly)
end
def test_methods_can_call_yield_many_times
result = []
many_yields { |item| result << item }
assert_equal [:peanut, :butter, :and, :jelly], result
end
分析第一段代码,argument 传入到method_with_block_arguments. 通过yield("Jim"), argument被赋值为“Jim”,复制后会继续执行do end 之间的assert代码。如果说第一种方式yield占位成为代码注入的话,这部分则有点类似于回调。
3. block call 调用
def method_with_explicit_block(&block)
block.call(10)
end
def test_methods_can_take_an_explicit_block_argument
assert_equal 20, method_with_explicit_block { |n| n * 2 }
add_one = lambda { |n| n + 1 }
assert_equal 11, method_with_explicit_block(&add_one)
end
这一段的关键代码在于block.call ,直接为代码块传入值,穿入后,代码块就执行,block应该称之为没有方法名的函数体,直接被当作参数传递到函数中。只不过这个无名函数不能通过 函数名:参数 的形式调用,而是通过一个间接的call方法向其中传入参数。