`
jack
  • 浏览: 390691 次
  • 来自: 温州
社区版块
存档分类
最新评论

疑问:yield到底是怎么运作的?

阅读更多
  最早看到yield时,就用c/C++中的概念对比了一番,发现最接近就是“宏代码的展开”。粗看起来,这样的理解是可以的。不过马上就有个问题出来了,就是定义域。宏代码的展开,要求展开后的代码处于被展开的位置同一个定义域,否则,相关变量和函数就会出现没有定义的错误。 不过从下面的ruby代码来看,yield没有这样的问题存在

class A
def self.test
yield
end
end
class B
def test0
puts "call from class B"
end
def test1
A.test {test0}
end
end
b = B.new
b.test1


如果以代码展开的角度来理解上面代码的运行,那么就应该出现test0 不是A的函数这样的错误提示。不过运行下来很成功,没有出现任何错误。

还有一种可能是ruby的函数调用,就像C++一样,默认附带一个this对象。

def test1
A.test {test0}
end


这段代码在执行时是不是被修改成了

def test1
A.test {@this.test0}
end


ruby如果真有这个功能,上面代码的执行似乎能够说的通。

不过上面都是猜测,看下《Programming Ruby - 2nd》第一次提到yield的时候得说明
引用
First, a block may appear only in the source adjacent to a method call; the block is written starting on the same line as the method call’s last parameter (or the closing parenthesis of the parameter list). Second, the code in the block is not executed at the time it is encountered. Instead, Ruby remembers the context in which the block appears (the local variables, the current object, and so on) and then enters the method. This is where the magic starts.
Within the method, the block may be invoked, almost as if it were a method itself, using the yield statement. Whenever a yield is executed, it invokes the code in the block.When the block exits, control picks back up immediately after the yield.1 Let’s start
with a trivial example.

def three_times
yield
yield
yield
end
three_times { puts "Hello" }
produces:
Hello
Hello
Hello


这段文字提到了3点。
第一,保存block相关的context
第二,block调用像函数本身的一部分
第三,有个神秘的control在操纵这一切
如果第一点,可以看作ruby给test0附加了个this对象,但是第二第三个就说不通了,如果只是代码展开,第二第三就不需要说明了。看来关于yield是代码展开的想法是完全错的。

那么从第三点来看,yield的确有个代码运行块(先这样叫着)切换的动作,最后运行完了又切换回来.
加入代码切换的说法之后,给出的例子代码似乎是这样运行的

1.运行A.test的上半部分直到遇到yield
2.写入block的context,同时使用block的context和A.test运行到一半的context,作为block的运行环境来运行block代码,
3.block运行完毕,清除block的context,然后继续运行A.test的下半部分。

古怪阿古怪,yield是真的这样运行的吗?

从前面yield的翻译讨论来看,yield带有切换,退让,让步这类的意思。
yield如果有执行代码切换的功能,多少带有超级微型线程的意思了。而且是主动切换,执行代码主体。yield完全是一个控制代码执行顺序的关键字了。真神奇。

分享到:
评论
31 楼 blackanger 2007-04-11  
想这么复杂干什么?自己会用就行了,把ruby的源码研究下就知道yield怎么运作的了,干嘛这么猜来猜去的
30 楼 cookoo 2006-11-10  
第二个yield在block里,意思是当block内调用这个块时再转给更外层的block, 也就是生成link_to_remote的那段。

这个bug你最好直接去问作者
29 楼 fyol 2006-11-09  
get_tree_data里面用了两个yield,我真的没看明白那是什么意思,请高手解释一下
28 楼 fyol 2006-11-09  
这段程序有个错误,关系到yield的调用,请各位帮忙看一下:
view里的调用:

<div id="mytree" class="mytree">
<% @ancestors = @item.ancestors.collect{|parent| parent.id} unless !@item.has_parent? %>
<% @items = Item.find(:all) %> 
<%= get_tree_data(@items,0){|n|
  link_to_remote n.name,
  :url=>{:controller=>'items',:action=>'display_clicked_item',:id=>n.id},
  :loading=>"Element.show('tree_indicator')",
  :complete=>"Element.hide('tree_indicator')"
  } %> 
  <% @items.each do |node| %> <%= draggable_element node.id.to_s+'_tree_div',:revert=>true,:snap=>false, :handle=>"'#{node.id.to_s}_drag_image'" %>
<%= drop_receiving_element(node.id.to_s+'_tree_div',
	:accept=>'inner_tree_element',
    :url=>{:controller=>'collection',
    :action=>'sort_my_tree',
    :parent_id=>node.id,:id=>nil},
    :loading=>"Element.show('sort_tree_indicator')",
    :complete=>"Element.hide('sort_tree_indicator');"
    )%> <% end %> 

<%= image_tag 'indicator.gif', 
    :id=>'sort_tree_indicator', 
    :style=>'display:none' %>

</div>


controller:
def get_tree_data(tree, parent_id)
    ret = "<div class='outer_tree_element' >"
    tree.each do |node|
      if node.parent_id == parent_id
        node.style = (@ancestors and @ancestors.include?(node.id))? 'display:inline' : 'display:none'
        display_expanded = (@ancestors and @ancestors.include?(node.id))? 'inline' : 'none'
        display_collapsed = (@ancestors and @ancestors.include?(node.id))? 'none' : 'inline'
        ret += "<div class='inner_tree_element' id='#{node.id}_tree_div'>"
        if node.has_children?
          ret += "<img id='#{node.id.to_s}expanded' src='/images/expanded.gif' onclick='javascript: return toggleMyTree(\"#{node.id}\"); ' style='display:#{display_expanded}; cursor:pointer;'  />  "
          ret += "<img style='display:#{display_collapsed}; cursor:pointer;'  id='#{node.id.to_s}collapsed' src='/images/collapsed.gif' onclick='javascript: return toggleMyTree(\"#{node.id.to_s}\"); '  />  "
        end
        
        ret += " <img src='/images/drag.gif' style='cursor:move' id='#{node.id}_drag_image' align='absmiddle' class='drag_image' /> "
        
        ret += "<span id='#{node.id}_tree_item'>"
        ret += yield node
        ret += "</span>"
        ret += "<span id='#{node.id}children' style='#{node.style}' >"
        ret += get_tree_data(node.children, node.id){|n| yield n}
        ret += "</span>"
        ret += "</div>"
      end
    end
  end

这段调用总是报错:

引用
Showing app/views/items/_ajax_tree.rhtml where line #71 raised:

can't convert Array into String

Extracted source (around line #71):

68: <div id="mytree" class="mytree">
69: <% @ancestors = @item.ancestors.collect{|parent| parent.id} unless !@item.has_parent? %>
70: <% @items = Item.find(:all) %>
71: <%= get_tree_data(@items,0){|n|
72:   link_to_remote n.name,
73:   :url=>{:controller=>'items',:action=>'display_clicked_item',:id=>n.id},
74:   :loading=>"Element.show('tree_indicator')",


Trace of template inclusion: /app/views/items/show.rhtml

RAILS_ROOT: ./script/../config/..

Application Trace | Framework Trace | Full Trace
#{RAILS_ROOT}/app/helpers/application_helper.rb:22:in `+'
#{RAILS_ROOT}/app/helpers/application_helper.rb:22:in `get_tree_data'
#{RAILS_ROOT}/app/helpers/application_helper.rb:5:in `get_tree_data'
#{RAILS_ROOT}/app/helpers/application_helper.rb:22:in `get_tree_data'
#{RAILS_ROOT}/app/helpers/application_helper.rb:5:in `get_tree_data'
#{RAILS_ROOT}/app/views/items/_ajax_tree.rhtml:71:in `_run_rhtml_items__ajax_tree'
#{RAILS_ROOT}/app/views/items/show.rhtml:8:in `_run_rhtml_items_show'

27 楼 aardvark 2006-11-04  
cookoo 写道
其实以前有人在ruby-lang列表里建议把yield改成call什么的,因为yield总是容易让人联想到线程,coroutine上面去。不知道Matz怎么想的。。。


Matz是从别人那抄来的。这么实现iterator是MIT一个女博士的发明:

http://en.wikipedia.org/wiki/CLU_programming_language

我没有仔细看过CLU,不知道在CLU里面yield的涵义是不是更宽一些。
26 楼 cookoo 2006-11-04  
其实以前有人在ruby-lang列表里建议把yield改成call什么的,因为yield总是容易让人联想到线程,coroutine上面去。不知道Matz怎么想的。。。
25 楼 whisper 2006-11-03  
python的yield和C# 2.0的yield语义一致
我觉得这完全就是个语法糖
很可能就是编译器生成了个 []
然后每次yield就push
最后返回放满了东西的列表
不过暂时没时间看他的源码了

ps. ruby和python的yield干的事都不一样,没必要比较了
24 楼 njmzhang 2006-11-02  
buaawhl 写道

从这一点来看, ruby yield 和 python yield 在 generator 部分,是一样的。


Ruby的yield不是generator。
你应该仔细看看aardvark和cookoo的两个fib函数,
Ruby的是while i1 <= max, Python的是while 1:。

Ruby的yield可以看是低开销的Proc#call, 而Python中的yield具有了一些continuation的特性
23 楼 buaawhl 2006-11-02  

sorry. 我前面是写错了。

我想写的是,aardvark的第一段代码中,
generator 部分 (就是 fibUpTo)维护了状态。
这正是 iterator 的 特点。

从这一点来看, ruby yield 和 python yield 在 generator 部分,是一样的。

不过,Ruby yield 的 receiver 部分是一个 block -- callback。这个 block 是不能保持状态的。
这点和 Python Yield 的 receiver 部分不一样。

aardvark的第二段代码,就表示了ruby yield generator + block receiver 的真实含义 -- 一个 visitor。block receiver 是一个 visitor callback function.

22 楼 whisper 2006-11-02  
那个我确实也是说ruby和python的yield不一样的
只是我的输入法比较难用
少打了个“不”
已经改正了
当时简直郁闷的要死
21 楼 cookoo 2006-11-02  
布娃娃:你理解错了,aardvark的例子里是调用函数fibUpTo在维护状态,被调用的block本身是无状态的。

whisper:我就是为了证明python的yield和ruby的yield不是一回事啊呵呵。你说的yield有点累赘也有道理,不过ruby的哲学就是同一件事有几种不同做法,所以各取所爱拉。

那段haskell的map定义写成ruby的话:
def map(a, &f)                                                               
  if a.empty? then [] else b,*c = a; map(c,&f).insert(0, f.call(b)) end
end

>>map([1,2,3]){|x| x+1}
=>[2,3,4]

                                                 
BTW, haskell确实是神的语言阿,我等凡人是没指望了                          
20 楼 roseanne 2006-11-01  
When I started to learn ruby, I felt that Ruby was too complicated, and many average programmers might not be able to understand and master it.

The people on this forum are really great, high/deep level developers. From the discussion above, which seems confirmed my thought to certain extent.


曲高和寡!
19 楼 buaawhl 2006-11-01  
aardvark和cookoo的 ruby yield 代码有些不同。

cookoo 的 yield 后面没有返回值。
aardvark 的 yield 后面带一个返回值。

从 aardvark 给出的两段对比代码来看,fibUpTo 好像记住了上次的 context。

right ?

根据 whisper 的回复,有些理解 aardvark 给出的两段对比代码。

ruby yield 的意思好像是等待一个 block 参数?

表面上看来是一个 iterator (不需要block参数,但是需要记住状态),实际上是一个 visitor(需要一个block参数,不需要记住状态)。

不过,我觉得,按照 iterator 来理解 yield 的表面意思,从用法上来说,也说得通。
18 楼 whisper 2006-11-01  
jack 写道
whisper 写道
yield机制更像是匿名lamda
就像C++中成员函数隐含this参数
ruby函数也有一个隐含参数
然后语法要求这个参数必须是lamda
yeild相当与调用这个匿名函数

用起来倒是不错
但个人感觉他太愚蠢
lamda已经非常优雅了
没必要使用这种折中模式


不太明白你的意思,你说c++中没有必要用yield还是ruby中?

我是觉得ruby没必要用这语法糖
如果一个函数内部yield了
那么证明他需要一个外部给定的参数来完成其内部工作
也就是说yield也是这个函数接口的一部分
如果是通过显示的多一个参数似乎更加的明确了函数的行为
而不是依赖于文档和约定俗成

像haskell那样的函数式语言已经很好的诠释了lamda的使用
其优雅程度比ruby有过之而无不及
所以这yield用起来虽爽
但心里总是有个疙瘩

map :: [a] -> (a -> b) -> [b]
map [] f = []
map (x: xs) f = (f x) : (map xs f)
不觉得这是神的语言么?
17 楼 fixopen 2006-11-01  
yield就是一个context saver+jump。没有多少神秘的地方,而最复杂的就是这个context了。

任何一段代码,其运行时,都有自己的context,提供这段代码中所有可以看到的变量,资源句柄,PC……,而这些东西一旦被保留下来,就可以把它认为是一个label了,可以实施非局部的jump了,而不再是stack式的后进先出,层层递进和消退的方式。

可以参见http://home.macau.ctm.net/~kewei/youbing/coroutine-iterator-in-c.html,里面有非常详细的说明。
16 楼 jack 2006-11-01  
whisper 写道
yield机制更像是匿名lamda
就像C++中成员函数隐含this参数
ruby函数也有一个隐含参数
然后语法要求这个参数必须是lamda
yeild相当与调用这个匿名函数

用起来倒是不错
但个人感觉他太愚蠢
lamda已经非常优雅了
没必要使用这种折中模式


不太明白你的意思,你说c++中没有必要用yield还是ruby中?
15 楼 whisper 2006-11-01  
cookoo 写道
布娃娃:context switch不足以表明coroutine,一般的方法调用(subroutine)也是context switch的,只有能保留上次context的(continuation)才是coroutine。Ruby的yield只是种匿名函数调用的syntax sugar。你用的yield 1, yield2, yield3的例子是用1,2,3去初始化块变量, block本身不记得上次的context:
>> def test2                 
>>   yield                   
>>   yield                   
>>   yield                   
>> end                     
=> nil                       
>> test2{x ||= 0; puts x+= 1}
1                            
1                            
1                            
=> nil


对比一下python的yield:
def fib():
  x, y = 0, 1
  while 1:
    x, y = y, x+y
    yield x

g = fib()
for i in range(9):
  print g.next(),  

显然python的generator是lazy的,不是简单的调用/返回。
ruby要完成同样的lazy效果的话,得借助于callcc实现的coroutine,光靠yield是办不到的。

*发完才发现楼上把我想说的先说了,所见略同呼呼。


ruby的yield跟python的yield根本就不是一个东西吧
14 楼 whisper 2006-11-01  
yield机制更像是匿名lamda
就像C++中成员函数隐含this参数
ruby函数也有一个隐含参数
然后语法要求这个参数必须是lamda
yeild相当与调用这个匿名函数

用起来倒是不错
但个人感觉他太愚蠢
lamda已经非常优雅了
没必要使用这种折中模式
13 楼 jack 2006-11-01  
aardvark和cookoo的回复,突然让我想到了coroutine和yield在c++项目中应用的可能,google下,还不少。在这里感谢各位。

12 楼 cookoo 2006-11-01  
布娃娃:context switch不足以表明coroutine,一般的方法调用(subroutine)也是context switch的,只有能保留上次context的(continuation)才是coroutine。Ruby的yield只是种匿名函数调用的syntax sugar。你用的yield 1, yield2, yield3的例子是用1,2,3去初始化块变量, block本身不记得上次的context:
>> def test2                 
>>   yield                   
>>   yield                   
>>   yield                   
>> end                     
=> nil                       
>> test2{x ||= 0; puts x+= 1}
1                            
1                            
1                            
=> nil


对比一下python的yield:
def fib():
  x, y = 0, 1
  while 1:
    x, y = y, x+y
    yield x

g = fib()
for i in range(9):
  print g.next(),  

显然python的generator是lazy的,不是简单的调用/返回。
ruby要完成同样的lazy效果的话,得借助于callcc实现的coroutine,光靠yield是办不到的。

*发完才发现楼上把我想说的先说了,所见略同呼呼。

相关推荐

    yield:yield是Riot.js v4的补充补丁,缺少一些使用在Riot.js v3中

    pkg yield yield是Riot.js v4的补充补丁,缺少在Riot.js v3使用&lt;yield&gt;实现的某些功能。 安装 对于npm用户, $ npm install @creatorqsf/yield 对于纱线使用者 $ yarn add @creatorqsf/yield 用法 在注册防暴...

    haxe-yield:用于Haxe的跨平台类似C#的`yield`生成器

    任何@yield表达式可用于以:yield元数据注释的类,或者可用于所有扩展以:yield(Extend)注释的类的类。 @ :yield class MyClass { // ... } 以下示例显示了yield元数据的两种形式: @ yield return expression ; @...

    Python yield的用法实例分析

    本文实例讲述了Python yield的用法。分享给大家供大家参考,具体如下: yield的英文单词意思是生产,刚接触Python的时候感到非常困惑,一直没弄明白yield的用法。 只是粗略的知道yield可以用来为一个函数返回值塞...

    yield总结与实例

    二、yield是一个语法糖,为方便开发者提供的一种方便方法 三、yield返回类型为IEnumerator、IEnumerable、IEnumerator、IEnumerable 四、如果返回类型为IEnumerator编译时会实现一个实现了IEnumerator接口的类 五、...

    java 线程让步(Yield)

    java 线程让步(Yield) java 线程让步(Yield) java 线程让步(Yield)

    yield表达式.pdf

    讲解Python的yield表达式,因为这个关键字平常不常用,也容易产生歧义,不易 理解;所以这里把它单独拿出来讲解,它与return的区别,它和生成器的关系,它的通常用法。

    基础算法-python打印杨辉三角

    yield b #使用yield a, b =b, a + b n = n + 1 N = int (input("请输入生成行数N: ")) def yanghui_triangles(): a = [1] while True: yield a #执行到yield b 时,fab函数返回一个迭代值 a = [sum(i) for i ...

    JS_Yield:JS yield原始学习-js

    JS_Yield JS yield原始学习

    Python 生成器,迭代,yield关键字,send()传参给yield语句操作示例

    本文实例讲述了Python 生成器,迭代,yield关键字,send()传参给yield语句操作。分享给大家供大家参考,具体如下: demo.py(生成器,yield关键字): # 生成器是一个特殊的迭代器。可以用for...in遍历。 # 带有...

    C#中yield return用法分析

    本文实例讲述了C#中yield return用法,并且对比了使用yield return与不使用yield return的情况,以便读者更好的进行理解。具体如下: yield关键字用于遍历循环中,yield return用于返回IEnumerable,yield break用于...

    Python yield 使用浅析

    初学 Python 的开发者经常会发现很多 Python 函数中用到了 yield 关键字,然而,带有 yield 的函数执行流程却和普通函数不一样,yield 到底用来做什么,为什么要设计 yield ?本文将由浅入深地讲解 yield 的概念和...

    PHP yield关键字功能与用法分析

    它最简单的调用形式看起来像一个return申明,不同之处在于普通return会返回值并终止函数的执行,而yield会返回一个值给循环调用此生成器的代码并且只是暂停执行生成器函数。 Example #1 一个简单的生成值的例子 &lt...

    C#中yield用法使用说明

    yield return &lt;expression&gt;; yield break; 备注: 计算表达式并以枚举数对象值的形式返回;expression 必须可以隐式转换为迭代器的 yield 类型。 yield 语句只能出现在 iterator 块中,该块可用作方法、运算符...

    Python 3中的yield from语法详解

    前言 最近在捣鼓Autobahn,它有给出个例子是基于asyncio 的,想着说放到pypy3上跑跑看竟然就……失败了...好吧这个标题是我google出来的,yield from的前世今生都在 这个PEP里面,总之大意是原本的yield语句只能将CPU控

    使用C# yield关键字来提高性能和可读性

    使用C# yield关键字来提高性能和可读性

    yield curve modelling

    yield curve modelling, interest rate models

    Python库 | ffmpeg_progress_yield-0.1.2-py2.py3-none-any.whl

    python库,解压后可用。 资源全名:ffmpeg_progress_yield-0.1.2-py2.py3-none-any.whl

    AEC-Q002B1:2012 Guidelines for Statistical Yield Analysis - 完整英文

    AEC-Q002B1:2012 Guidelines for Statistical Yield Analysis - 完整英文电子版(6页).zip

    PHP5.5新特性之yield理解与用法实例分析

    yield生成器允许你 在 foreach 代码块中写代码来迭代一组数据而不需要在内存中创建一个数组。 使用示例: /** * 计算平方数列 * @param $start * @param $stop * @return Generator */ function squares($...

    yield curve

    source code about yield curve

Global site tag (gtag.js) - Google Analytics