Ruby 关于 yield 的问题

tony · 2011年11月01日 · 最后由 bluesky0318 回复于 2016年06月23日 · 12178 次阅读

有这么一段代码

def initialize(api_key, secret_key, options={})
  @api_key=api_key
  @secret_key=secret_key
  @oauth_request_option = @@default_oauth_request_options.merge(options)
  @oauth_access_option = @@default_oauth_access_options.merge(options)
  yield self if block_given?
  self
end 

不太明白其中的

yield self if block_given?
self

是什么意思

我看了 https://gist.github.com/1152377 的例子 但还是不太明白为什么要这么写,或者在什么情况下需要用 yield

当你需要这样调用方法的时候

def foo
end

foo { "this is block param." }

现实点的场景,比如你需要给 Views 定一个 space_less Helper 方法,清除区域内的 HTML 的空白字符,使得调用起来可以这样:

<%= spaceless do %>
<html>
<body>
  <head></head>
</body>
<% end %>

这个时候,你就需要用 yield 来定义一个可以接收 block 作为参数的方法了

module ApplicationHelper
  def splace_less
     body = yield
     body.gsub!(/\s+/,'')
     body
  end
end

感觉好像有点像回调函数 之前一直写 php 的,理解起来有点困难 搜到一种解释 def foo yield end foo {puts 'test'}

等同于

def foo &block
&black.call
end
foo {puts 'test'}

理解了一点,感觉这是个很强大的功能

确实是类似回调函数,就是一种代码注入 , ruby 不像 js 那样直接传 function , 代码作为参数都是靠 block 或者 proc/lambda

应用场景

content = 'hi, it's content'

File.open('tmp/test', 'w') do |f|
  f.write content
end

既能运行在 File.open 的上下文,又能拿到 content 这个变量。这个调用的好处是 File 对象会在 block 运行后自动关闭。也就是 yield 给了一种方便的带着变量切换上下文(包括所含有的方法)的能力,实现什么 DSL 就看程序员的想象力了。

比如 xmlbuilder

xml do |xml|
  xml.article do |a|
    a.author 'Rei'
  end
end

block , proc , lambda ruby 里的闭包 , yield 是调用 block , proc 和 lambda 可以当作参数传到方法中,并且可以获得函数中的上下文,和 js 中的匿名函数相似

DEMO:

#!/usr/bin/env ruby

def test _proc_,&block
_proc_.call
yield 
end

a = Proc.new { puts "blabla" }

test a ,do puts "HI" end

=======>

blabla
HI

#1 楼 @huacnlee
哇,很少看到华顺,这么长的回复啊~

本来我也想给楼主答案,不过注意到帖子是六个月前,还是算了...

#2 楼 @tony &black.call 貌似这里笔误了。应该是 block.call

可以理解为宏,或者常量定义

需要 登录 后方可回复, 如果你还没有账号请 注册新账号