Ruby yield 关键字的使用

msms · 2013年07月09日 · 最后由 blackanger 回复于 2013年07月26日 · 4849 次阅读

今天在做测试的时候,对于以下代码:

def test_yield (select_locale,value,locale)
  if select_locale == locale
      yield value
  end
 end
 test_yield("locale", ["name","desc"], "locale") do |arr,locale|
  puts arr.class.to_s   # 居然是 String,而不是 Array
  puts arr.to_s
  puts locale
 end
 test_yield2("locale", {:name=>"name",:desc=>"desc"}, "locale") do |arr,locale|
  puts arr.class.to_s   
  puts arr.to_s
  puts locale
 end

test_yield 用 Array 会出错,test_yield2 用 Hash 就不会出错, 那位大神能解释下问什么捏?

def test_yield 后面多了个空格。

可有可无的括号最好去掉

改了 yield value,locale

block 是两个参数,你传了一个进去

def test_yield (select_locale,value,locale)
  if select_locale == locale
    yield value,locale # 需要两个参数?
  end
end
test_yield("locale", ["name","desc"], "locale") do |arr,locale|
  puts arr.class.to_s
  puts arr.to_s
  puts locale
end

#2 楼 @ihlayy 这个只是其中的一部分,在 select_locale == locale 是需要省略掉 locale 的,这只是一个 Example

#2 楼 @ihlayy 代码块和缩进式怎么搞的

#4 楼 @Msms 我意思是说,参数个数不对应造成的,缩进看发帖的按钮附近有"帮助",markdown 标记

#4 楼 @Msms

def test_yield select_locale, value, locale

就好了...

ruby def test_yield select_locale, value, locale

#5 楼 @ihlayy 这个我知道,不过貌似数组改成 Hash 表就可以了,具体原因不清楚,有大神能指教下麽

#4 楼 @Msms 代码块我来试试

执行结果应该是 Array 而不是 Hash, 所以你说的换成 Hash 是对的,是不成立的

arr = [:name=>"name",:desc=>"desc"]
puts arr.class.to_s   
puts arr.to_s

挺有趣的现象,不过也很容易说明,#2 楼 @ihlayy 是正解 问题在于 yield 传入的参数被 Multiple Assignment 到 arr, locale 上了,把 yield 修改一下即可

def test_yield(select_locale,value,locale)
  if select_locale == locale
    yield [value]
  end
end
test_yield("locale", ["name","desc"], "locale") do |arr,locale|
  puts arr.class.to_s   # 现在就是Array了
  puts arr.to_s
  puts locale
end
匿名 #11 2013年07月10日

test_yield 里面 arr 和 locale 分别对应"name","desc" test_yield2 里面 arr 和 locale 分别对应是 {:desc=>"desc", :name=>"name"}和 nil

#11 楼 @cdrwin 这个现在已经知道了,不过具体原因是什么呢,Hash 和 Array 在传入参数的时候有什么不同么

#10 楼 @fsword 如果 test_yield 在系统中别的地方还在被使用,最好不要动这个函数,然后还有别的修正方法么?

#13 楼 @Msms 那就在 block 里面做文章呗

def test_yield(select_locale,value,locale)
  if select_locale == locale
    yield value
  end
end

test_yield("locale", ["name","desc"], "locale") do |arr|
  puts arr.class.to_s   # 这样也是Array
  puts arr.to_s
  locale = arr.last
  puts locale
end

#13 楼 @Msms

其实你仔细观察会发现,传入 block 的时候,如果参数是一个 Array,则 Array 里面的参数与 block 的 参数一一对应。我猜测 Array 被当成可变长度参数传递了?分析

test_yield("locale", ["name","desc"], "locale") do |arr,locale|
    puts arr.class.to_s  
    puts arr.to_s
    puts locale
   end

会发现其实 arr => "name" locale => "desc"

所以 arr.class.to_s 为 String 而不是 Array.

但是根据推断,执行下面的代码结果又会让你大吃一惊

test_yield("locale", ["name","desc"], "locale") do |arr|
    puts arr.class.to_s  
    puts arr.to_s
end

打出的是 Array,"name",

再如

def test_yield (select_locale,value,locale)
    if select_locale == locale
        yield locale,value
    end
end

test_yield("locale", ["name","desc"], "locale") do |arr,aaa,bbb|
  puts arr.class.to_s 
  puts arr.to_s
  puts aaa.to_s
  puts bbb.to_s
end

再如

def test_yield (select_locale,value,locale)
    if select_locale == locale
        yield value,locale
    end
end

test_yield("locale", ["name","desc"], "locale") do |arr,aaa,bbb|
  puts arr.class.to_s 
  puts arr.to_s
  puts aaa.to_s
  puts bbb.to_s
end

结合上面的例子,你会发现,当你往 block 传递参数的时候 有且只有一个参数,且参数为数组 a. 如果 block 只有一个参数,则数组会当成一个参数, b. 如果 block 的参数大于传递进来的参数,数据里会当成可变参数

如果有多个参数 a.所有的参数和 block 依次对应

所以我不建议你这么写,你这么写可能不是一个规范的写法

有时间可以查查关于 Block 的资料,我还没来得及看 - - http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_methods.html

#15 楼 @ihlayy 好的,我来看看,非常感谢!

17 楼 已删除

@Msms 晕了。 yield 的作用是「转让」块代码,块代码可以看做是一个匿名函数。记住这一条原则就可以了。Ruby 灵活多变,我们需要抓住一条不变的实质,才能处理一些问题。否则的话,看在眼里都是奇怪的结果。

test_yield("locale", ["name","desc"], "locale") do |arr, locale|
    puts arr.class.to_s  
    puts arr.to_s
end

相当于


def test_yield (select_locale,value,locale)

      #经过yield,把块代码压入方法堆栈
      f(arr, locale){ 
          puts arr.class.to_s
          puts arr.to_s
     } 

yield value 这里 yield 后面的参数就是传递给块那个匿名方法参数的。所以要注意参数个数(块里的参数)。

@ihlayy 总结的也很好,只是感觉没说到点子上。

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