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

疑问: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是办不到的。

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

相关推荐

    Pb中Yield()函数的使用[文].pdf

    Pb 中 Yield() 函数的使用 Yield() 函数是 PowerBuilder 中一个非常有用的函数,它可以将控制权转移给其他图形对象,包括非 PowerBuilder 对象。该函数检测消息队列,如果有消息,就把消息取出。该函数返回布尔型值...

    yield表达式.pdf

    Python中的yield表达式是一个极为重要的概念,它是生成器(generator)的核心组成部分。生成器在处理大量数据时显得尤为有用,因为它可以逐个产生数据项而不是一次性加载所有数据到内存中。这不仅可以节省内存,还能...

    java 线程让步(Yield)

    Java线程让步(Yield)是Java多线程编程中的一个重要概念,它涉及到线程调度和并发控制。在多线程环境下,线程让步意味着一个正在运行的线程主动放弃当前的时间片,以便其他就绪状态的线程有机会执行。这与线程的...

    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 ; @...

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

    使用C# yield关键字来提高性能和可读性 C# 中的 yield 关键字可以提高代码的性能和可读性。yield 关键字可以让开发者使用惰性枚举(Lazy Enumeration),从而减少不必要的内存分配和数据处理。下面我们将详细介绍 ...

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

    《AEC-Q002B1:2012 Guidelines for Statistical Yield Analysis》是一份针对半导体行业统计产量分析的重要指导文档。这份文档详尽地阐述了如何在2012年的背景下,运用统计方法对生产过程中的良品率进行评估和优化。...

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

    Python库`ffmpeg_progress_yield`是用于处理多媒体文件的工具,特别是在视频和音频处理方面。它是一个基于Python的接口,能够与FFmpeg命令行工具进行交互,从而为用户提供更方便、更高级别的API来操作多媒体数据。`...

    Python yield的用法实例分析

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

    yield总结与实例

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

    JS_Yield:JS yield原始学习-js

    JavaScript中的`yield`关键字是Generator函数的核心特性,用于在异步操作中实现暂停和恢复执行。Generator函数是ES6引入的一种新的函数类型,它解决了回调地狱的问题,为异步编程提供了一种更优雅的方式。 ...

    Unity3D教程:后台资源加载与yield用法2

    Unity3D教程:后台资源加载与yield用法2 Unity3D教程:后台资源加载与yield用法2是 Unity3D开发中一个重要的知识点,涉及到后台资源加载和yield的使用。本文将详细介绍yield的核心功能和使用方法,以及在 Unity3D...

    基础算法-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 ...

    python异步编程 使用yield from过程解析

    `yield from`语句是Python 3.3引入的新特性,它在异步编程中扮演着重要角色,主要用于简化生成器(generator)的使用,尤其是处理嵌套生成器的情况。 首先,让我们理解`yield from`的基本作用。在Python中,生成器...

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

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

    C#中Task.Yield的用途深入讲解

    C#中Task.Yield的用途深入讲解 Task.Yield是C#中的一个重要概念,它可以帮助开发者更好地管理线程资源,提高程序的性能和可维护性。本文将深入讲解Task.Yield的用途,通过示例代码和详细的解释,帮助读者更好地理解...

    yield curve modelling

    ### 收益曲线建模(Yield Curve Modeling) #### 知识点一:收益曲线背景 收益曲线(Yield Curve)是一种重要的金融工具,它显示了不同期限的债券收益率与其到期时间之间的关系。通常情况下,收益曲线是按照债券的...

    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 的概念和...

Global site tag (gtag.js) - Google Analytics