es6 yield 使用在递归中怎么使用

====python====(17)
1 递归中使用yield
注意 递归算法中若要使用生成器,需要在生成器的原函数(首次调用)显式得到所有yield值
def quick_sort(parameter_list):
if len(parameter_list) == 0:
if len(parameter_list) == 1:
yield parameter_list[0]
if parameter_list[0] & parameter_list[-1]:
pivot = parameter_list[0]
left = [i for i in parameter_list if i & pivot]
right = [i for i in parameter_list if i & pivot]
for item in quick_sort(left):
yield item
yield pivot
for item in quick_sort(right):
yield item
2 yield中递归返回
当我们持有一个generator之后,我们可以做什么呢?
我们可以获得它的输出,并且给它传值。这里,隐含了一种特殊的模式,generator输出任务,而我们传入任务的执行结果。这个过程看起来就像一次函数调用一样:)
这里最关键的一点是,我们持有了任务的处理过程,而generator并不关心任务是如何被处理的。实际上,我们可以直接执行,或者丢给异步引擎,或者丢到线程池,或者干脆提交到远程机器上执行。我们把这个分配任务的部分叫做调度器吧:)
如果generator生成的任务本身也是generator呢?我们应当如何让generator完成递归呢?
如何返回值?
其实我们直接用yield返回值即可。但是我们需要把任务和值区分开来,因为python并没有提供什么有效的方式能把这两者分离。所以我们通过yield出的值的类型判断即可,我们可以定义一个接口,所有持有某个方法的对象都是任务。
调度器的困境
这里有一个重要的问题是,也就是generator的下一次操作,取决于任务是否完成。而这个操作是由任务决定的,调度器无法做到这一点。这导致一个问题,调度器不能直接控制generator的执行,它需要把控制的操作下传给任务,让任务在结束后自动完成这个操作。只有这样,调度器才不需要独立的线程或者额外的方式进行控制,因为它的触发是被动的。
实现如下功能计算斐波那契数列
def fib(n):
if n &= 1:
yield (yield RecTask(fib, n-1)) + (yield RecTask(fib, n-2))
pfib = rec_gen(fib, lambda x: print(x))
for i in range(15):
任务的执行需要接受一个callback和一个error_callback函数,当任务完成的时候,它执callback,出现错误则执行error_callback。
我们需要把对于generator的控制封装到一个callback中,使得任务可以调用这个函数,完成它的功能。我们可以把重试的函数作为error_callback传入任务,这使得任务在失败之后可以被重试。
下面是一个实现。包含了如下的一些特性。
调度器可以作为装饰器使用(如果忽略错误和返回值)
对于正常的函数,它能够直接返回结果(传递给callback)
对于普通的generator输出,它能把generator的输出作为参数传递给callback
所有的任务对象都包含一个dojob方法,它接受callback和err_callback,用于完成任务
任务包含了重试机制,当任务失败次数达到限额之后,整个任务会直接失败(整个递归过程)
RecTask任务
默认的RecTask类型,是常规的python函数调用,包含函数和它的参数。
RecTask方法包含一个transform方法,用于对任务函数进行变换(完全是一个约定)。这个方法不会修改原始的任务函数,因为一个可变对象在重复调用的过程中会出现难以预计的问题。
RecTask的run方法,用于接受新的任务函数(如果是None则直接执行原始的函数)
通过继承RecTask,可以构造其它的任务执行方式。比如通过异步引擎来执行任务。这也使得异步架构能够完成一些递归任务:)
具体代码实现
import sys
import types
from traceback import format_exc
def rec_gen(func, callback=None, err_callback=None):
callback accept arguments with output of func
err_callback is called after Exception occured, accept Exception instance as it's arguments
def trans_func(*args, **kwargs):
def error_do(e):
print('@rec_func_error:', e, file=sys.stderr)
if err_callback is not None:
err_callback(e)
g = func(*args, **kwargs)
except Exception as e:
error_do(e)
if not isinstance(g, types.GeneratorType):
if callback is not None:
callback(g)
def go_through(it=None):
em = g.send(it)
if not hasattr(em, 'dojob'):
go_through(None)
em.dojob(callback=go_through, err_callback=error_do)
except StopIteration as st:
if callback is not None:
callback(st.value)
except Exception as e:
error_do(e)
go_through()
return trans_func
from functools import partial
class RecTask(object):
def __init__(self, func, *args, **kwargs):
self.func = func
self.args = args
self.kwargs = kwargs
def dojob(self, callback=None, err_callback=None):
self.run(self.transform(partial(rec_gen, callback=callback, err_callback=err_callback)))
def transform(self, f):
return f(self.func)
def run(self, func=None):
if func is None:
func = self.func
return func(*self.args, **self.kwargs)
class TaskWithRetry(RecTask):
retry_limit = 1
def dojob(self, callback=None, err_callback=None):
try_cnt = 0
def ierr(e, *args, **kwargs):
nonlocal try_cnt
if not hasattr(self, 'retry_limit') or try_cnt & self.retry_limit:
print('@error: overflow retry limit! task has complete failed', file=sys.stderr)
if err_callback is not None:
return err_callback(e, *args, **kwargs)
try_cnt += 1
print('@warning_retry: retry count: %s, %s' % (try_cnt, e), file=sys.stderr)
self.run(self.transform(partial(rec_gen, callback=callback, err_callback=ierr)))
self.run(self.transform(partial(rec_gen, callback=callback, err_callback=ierr)))
class MapTask(object):
def __init__(self, *tasks):
self.tasks = list(tasks)
def dojob(self, callback=None, err_callback=None):
self.ans = [None for e in self.tasks]
self.flags = [False for e in self.tasks]
t = len(self.tasks)
self.todo = callback
self.apply_tasks(err_callback=err_callback)
def apply_tasks(self, err_callback):
for i, e in zip(range(len(self.tasks)), self.tasks):
e.dojob(callback=self.acker(i), err_callback=err_callback)
def acker(self, posi):
def ack(x):
if self.flags[posi] is False:
self.flags[posi] = True
self.ans[posi] = x
if t == 0:
if self.todo is not None:
self.todo(tuple(self.ans))
return ack
class HTTPTask(TaskWithRetry):
def __init__(self, sender, req, callback):
self.sender = sender
self.req = req
self.callback = callback
self.retry_limit = 10
def transform(self, f):
return f(self.callback)
def run(self, callback=None):
if callback is None:
callback = self.callback
self.sender(self.req, callback)
if __name__ == '__main__':
sys.setrecursionlimit()
def fib(n):
if n &= 1:
return (yield RecTask(fib, n-1)) + (yield RecTask(fib, n-2))
pfib = rec_gen(fib, lambda x: print(x))
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:13296次
排名:千里之外
原创:40篇
转载:16篇
(2)(7)(3)(5)(12)(9)(5)(4)(6)(5)帐号:密码:下次自动登录{url:/nForum/slist.json?uid=guest&root=list-section}{url:/nForum/nlist.json?uid=guest&root=list-section}
贴数:11&分页:频谱发信人: commonsense (频谱), 信区: Python
标&&题: [求助]新手问题有关yield
发信站: 水木社区 (Sun Feb 28 11:18:53 2016), 转信 && 很简单,把一个嵌套的list捋成非嵌套的输出,是python基础编程 书内的例子,代码本身理解没问题,问题在于输出,见执行结果下面中文发问 && nested = [7,[[1],[6,2]],[3,4],[5]]
def flatten(nested): &&&& try: &&&&&&&& for sublist in nested: &&&&&&&&&&&& print "sub = ", sublist &&&&&&&&&&&& for element in flatten(sublist): &&&&&&&&&&&&&&&& print "element = ",element &&&&&&&&&&&&&&&& yield element &&&& except TypeError,t: &&&&&&&& print TypeError, ":", t &&&&&&&& print "nested = ",nested &&&&&&&& yield nested && #for i in flatten(nested): print i
#print list(flatten(nested)
f=flatten(nested)
print "1,",f.next()
print "2,",f.next() && 执行结果
/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7 /Users/charisma/PycharmProjects/untitled/nest.py
1, sub =&&7
&type 'exceptions.TypeError'& : 'int' object is not iterable
nested =&&7
element =&&7&&&&#请问下这句话为什么会被打出来?我的理解,产生typeError了以后,直接在except 后面yield nested,即7,为什么还会走到打印 element = 7这一行?
2, sub =&&[[1], [6, 2]]
sub =&&[1]
&type 'exceptions.TypeError'& : 'int' object is not iterable
nested =&&1
element =&&1
element =&&1
element =&&1
1 && Process finished with exit code 0 && -- && ※ 来源:·水木社区 ·[FROM: 123.116.51.*]
马鸣发信人: maming (马鸣), 信区: Python
标&&题: Re: [求助]新手问题有关yield
发信站: 水木社区 (Sun Feb 28 12:40:39 2016), 转信 && 你把没一步写出来。
可以设nested = [7]。 && 算flatten([7])不会明显触发error,而是是算 flatten(7)出现的error。所以element那句还会出现。 && 【 在 commonsense 的大作中提到: 】
: 很简单,把一个嵌套的list捋成非嵌套的输出,是python基础编程 书内的例子,代码本身理解没问题,问题在于输出,见执行结果下面中文发问
: nested = [7,[[1],[6,2]],[3,4],[5]]
: def flatten(nested):
: ...................
※ 修改:·maming 于 Feb 28 12:41:04 2016 修改本文·[FROM: 123.126.85.*]
※ 来源:·水木社区 ·[FROM: 123.126.85.*]
频谱发信人: commonsense (频谱), 信区: Python
标&&题: Re: [求助]新手问题有关yield
发信站: 水木社区 (Sun Feb 28 17:59:43 2016), 站内 && 就是这里不明白啊,我认为element和nested不应该同时打出来啊
对yield的理解没到位
-- && ※ 来源:·水木社区 ·[FROM: 123.116.51.*]
部落发信人: lokta (部落), 信区: Python
标&&题: Re: [求助]新手问题有关yield
发信站: 水木社区 (Sun Feb 28 19:37:43 2016), 转信 && pycharm单步debug就知道了。
地一个循环,出来个7
第二个循环,for element in flatten(sublist),进入第二个递归里
7不是list,触发错误处理,但是你这里面yield,for element in flatten(sublist)这里继续往下走,打印element.很清楚。 && 【 在 commonsense 的大作中提到: 】
: 很简单,把一个嵌套的list捋成非嵌套的输出,是python基础编程 书内的例子,代码本身理解没问题,问题在于输出,见执行结果下面中文发问
: nested = [7,[[1],[6,2]],[3,4],[5]]
: def flatten(nested):
: ...................
&& -- && ※ 来源:·水木社区 ·[FROM: 222.86.80.*]
频谱发信人: commonsense (频谱), 信区: Python
标&&题: Re: [求助]新手问题有关yield
发信站: 水木社区 (Mon Feb 29 09:37:14 2016), 转信 && 我用的是pycharm社区板,好像step into这些菜单项从来都是灰色的
所以我才加这么多打印 && 【 在 lokta 的大作中提到: 】
: pycharm单步debug就知道了。
: 地一个循环,出来个7
: 第二个循环,for element in flatten(sublist),进入第二个递归里
: ...................
&& -- && ※ 来源:·水木社区 ·[FROM: 124.207.127.*]
部落发信人: lokta (部落), 信区: Python
标&&题: Re: [求助]新手问题有关yield
发信站: 水木社区 (Mon Feb 29 09:59:22 2016), 转信 && 我也是社区版。
【 在 commonsense 的大作中提到: 】
: 我用的是pycharm社区板,好像step into这些菜单项从来都是灰色的
: 所以我才加这么多打印
:&& && -- && ※ 来源:·水木社区 ·[FROM: 222.86.80.*]
我的月份又来了发信人: JulyClyde (我的月份又来了), 信区: Python
标&&题: Re: [求助]新手问题有关yield
发信站: 水木社区 (Mon Feb 29 10:35:29 2016), 转信 && 不至于灰色吧?
不过加打印是好习惯,八荣八耻
建议改为logging.debug
【 在 commonsense (频谱) 的大作中提到: 】
: 我用的是pycharm社区板,好像step into这些菜单项从来都是灰色的
: 所以我才加这么多打印
那只是我的过去
我现在选择了自由
【 在 josephpei (linux) 的大作中提到: 】
: 【 在 JulyClyde (torred) 的大作中提到: 】
: : 当年我还是asp专家呢,现在不也转到Linux来了!
&&&& ※ 来源:·水木社区 newsmth.net·[FROM: 103.37.140.*]
频谱发信人: commonsense (频谱), 信区: Python
标&&题: Re: [求助]新手问题有关yield
发信站: 水木社区 (Mon Feb 29 16:29:12 2016), 转信 && 我刚刚单步了一下,现在看明白递归是是怎么回事了
但是如果flattedn两处yield,外面调用f.next()的时候到底以哪一次为准? && nested = [8] && def flatten(nested): &&&& try: &&&&&&&& for sublist in nested: &&&&&&&&&&&& print "here" &&&&&&&&&&&& for element in flatten(sublist): &&&&&&&&&&&&&&&& print "element = ",element &&&&&&&&&&&&&&&& yield element&& //此处yield &&&& except TypeError: &&&&&&&& print "nested = ",nested &&&&&&&& yield nested&& //此处yield && f = flatten(nested)
print f.next() && 【 在 maming 的大作中提到: 】
: 你把没一步写出来。
: 可以设nested = [7]。
: 算flatten([7])不会明显触发error,而是是算 flatten(7)出现的error。所以element那句还会出现。
: ...................
&& -- && ※ 来源:·水木社区 ·[FROM: 124.207.127.*]
马鸣发信人: maming (马鸣), 信区: Python
标&&题: Re: [求助]新手问题有关yield
发信站: 水木社区 (Mon Feb 29 16:46:43 2016), 转信 && 最外面只有一次yield,里面那个已经被 for element 吃掉了 && 【 在 commonsense 的大作中提到: 】
: 我刚刚单步了一下,现在看明白递归是是怎么回事了
: 但是如果flattedn两处yield,外面调用f.next()的时候到底以哪一次为准?
: nested = [8]
: ...................
&& -- && ※ 来源:·水木社区 ·[FROM: 111.202.249.*]
cpp发信人: ilovecpp (cpp), 信区: Python
标&&题: Re: [求助]新手问题有关yield
发信站: 水木社区 (Mon Feb 29 17:17:37 2016), 转信 && 先执行到谁算谁。 && 【 在 commonsense 的大作中提到: 】
: 我刚刚单步了一下,现在看明白递归是是怎么回事了
: 但是如果flattedn两处yield,外面调用f.next()的时候到底以哪一次为准?
: nested = [8]
: ...................
-- && ※ 来源:·水木社区 ·[FROM: 180.157.202.*]
文章数:11&分页:> 使用glob与yield递归索引文件,结果不对
使用glob与yield递归索引文件,结果不对
handongjiang & &
发布时间: & &
浏览:3 & &
回复:3 & &
悬赏:0.0希赛币
使用glob与yield递归目录文件,结果不对我想使用glob与yield递归目录下所有文件,结果不正确.
  def&browse_files(path):
&&&&for&f&in&glob.glob(path&+&os.sep&+&"*"):
&&&&&&&&if&os.path.isdir(f):
&&&&&&&&&&&&browse_files(f)
&&&&&&&&else:
&&&&&&&&&&&&yield&f&#此处使用yield返回文件名
path&=&r'E:\WIFI\MAC\1100old'
for&filename&in&browse_files(path):
&&&&print&filename
得到的结果(错误)是:
E:\WIFI\MAC\1100old\00_19_3B_82_3A_6B.lic
E:\WIFI\MAC\1100old\00_19_3B_82_3A_6D.lic
E:\WIFI\MAC\1100old\old.tar.gz
我把yield&f&改成&print&f
执行browse_files(path),得到结果(正确):
E:\WIFI\MAC\1100old\00_19_3B_82_3A_6B.lic
E:\WIFI\MAC\1100old\00_19_3B_82_3A_6D.lic
E:\WIFI\MAC\1100old\old\00_19_3B_81_DB_38.lic
E:\WIFI\MAC\1100old\old\00_19_3B_81_DB_3A.lic
E:\WIFI\MAC\1100old\old\00_19_3B_82_3A_6B.lic
E:\WIFI\MAC\1100old\old\00_19_3B_82_3A_6D.lic
E:\WIFI\MAC\1100old\old.tar.gz
请问错误在哪里,求指点
return&browse_files(f)
干嘛不用&walk()& handsome_sky & &
14:54:04 & &
& & (0)(0)引用if&os.path.isdir(f):
&&&&for&f&browse_files(f):
&&&&&&&&yield&f
好像看到最近的3.x版本有新语法,末2句可简化成&yield&from&browse_files(f)handsome_sky & &
14:54:04 & &
& & (0)(0)引用
本问题标题:
本问题地址:
温馨提示:本问答中心的任何言论仅代表发言者个人的观点,与希赛网立场无关。请对您的言论负责,遵守中华人民共和国有关法律、法规。如果您的言论违反希赛网问答中心的规则,将会被删除。
暂无合适的专家
&&&&&&&&&&&&&&&
希赛网 版权所有 & &&

我要回帖

更多关于 nodejs 如何使用yield 的文章

 

随机推荐