锁定老帖子 主题:疑问:yield到底是怎么运作的?
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2006-10-31
thx,布娃娃同学。你的睿智能够照亮javaeye的每一个角落
|
|
返回顶楼 | |
发表时间:2006-11-01
我觉得布娃娃同学在这个问题上被误导了,把简单问题复杂化了。如qiezi所说,yield在ruby里面就是个Proc#call。
用来解释coroutine的最好的例子就是producer/consumer问题。producer和consumer这两个coroutine是互相yield的,而iterator和block之间是单向的,block只有一个入口和一个出口,所以其实block是一个subroutine。 上面是从iterator和block的关系来说。从yield本身的能力来说,也没有办法用它实现来回切换context,换句话说不能用yield来实现coroutine。在ruby中coroutine必须用callcc来实现。这个似乎和其他语言不一样。我对别的语言并不很清楚,但是从读到的一些伪代码来看,似乎别的语言是可以用yield来做coroutine的。 yield的涵义更宽一些,是和coroutine关联的;call的涵义要窄一些,是和subroutine关联的。本来这个地方用call是很容易理解的,但是用了yield就让人想太多。 另外,threading里面也有个yield概念,就更容易让人困惑了。multi-threading是操作系统、虚拟机的概念,关注的是程序如何“同时”运行;而coroutine的yield概念是语言层面的,关注的是语句执行的顺序。threading的yield,就是“让”,把CPU资源让出来,让给谁了,不知道。而Ruby的yield,虽然不带参数,我们很清楚是让给block了,而且block一定会返回回来,并且带个返回值。 说完概念再看实际例子吧 def fibUpTo(max) i1, i2 = 1, 1 # parallel assignment while i1 <= max yield i1 i1, i2 = i2, i1+i2 end end fibUpTo(1000) { |f| print f, " " } 不用yield的等价实现就是 def fibUpTo(max,&block) i1, i2 = 1, 1 # parallel assignment while i1 <= max block.call i1 i1, i2 = i2, i1+i2 end end fibUpTo(1000) { |f| print f, " " } end 在第二个实现中,block.class 实际是Proc。这一点可以加个 puts block.class来证实。 所以,很简单,yield就是Proc#call,就是调用block这个没有名字的subroutine。 |
|
返回顶楼 | |
发表时间: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是办不到的。 *发完才发现楼上把我想说的先说了,所见略同呼呼。 |
|
返回顶楼 | |
发表时间:2006-11-01
aardvark和cookoo的回复,突然让我想到了coroutine和yield在c++项目中应用的可能,google下,还不少。在这里感谢各位。
|
|
返回顶楼 | |
发表时间:2006-11-01
yield机制更像是匿名lamda
就像C++中成员函数隐含this参数 ruby函数也有一个隐含参数 然后语法要求这个参数必须是lamda yeild相当与调用这个匿名函数 用起来倒是不错 但个人感觉他太愚蠢 lamda已经非常优雅了 没必要使用这种折中模式 |
|
返回顶楼 | |
发表时间: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根本就不是一个东西吧 |
|
返回顶楼 | |
发表时间:2006-11-01
whisper 写道 yield机制更像是匿名lamda
就像C++中成员函数隐含this参数 ruby函数也有一个隐含参数 然后语法要求这个参数必须是lamda yeild相当与调用这个匿名函数 用起来倒是不错 但个人感觉他太愚蠢 lamda已经非常优雅了 没必要使用这种折中模式 不太明白你的意思,你说c++中没有必要用yield还是ruby中? |
|
返回顶楼 | |
发表时间: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,里面有非常详细的说明。 |
|
返回顶楼 | |
发表时间: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) 不觉得这是神的语言么? |
|
返回顶楼 | |
发表时间: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 的表面意思,从用法上来说,也说得通。 |
|
返回顶楼 | |