前言
什么时候可以认真看过一遍SICP啊 (:3」∠)
以下单纯的作为学习笔记
迭代器
好像没什么徒手撸一个迭代器的机会所以留意一下Python的for循环本质上就是通过不断调用next()
函数实现。next()
方法返回容器的下一个元素,在结尾时引发StopIteration
异常
1 2 3 4 5 6 7 8 9 10
| # 首先获得Iterator对象: it = iter([1, 2, 3, 4, 5]) # 循环: while True: try: # 获得下一个值: print(next(it)) except StopIteration: # 遇到StopIteration就退出循环 break
|
yield的用法
1 2 3 4 5 6 7 8 9 10 11 12
| def fab(): a = 0 b = 1 while True: yield b a, b = b, a+b a = fab() a.__next__() #1 a.__next__() #1 a.__next__() #2 a.__next__() #3
|
上面生成了一个可返回无限长度的生成器函数(语言不太精确但大概就是这个意思),用python3的next()方法可以不断返回下一个值。
这里再贴一下itertools
里面islice()
的用法
islice()
的设定是
Make an iterator that returns selected elements from the iterable.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| def islice(iterable, *args): # islice('ABCDEFG', 2) --> A B # islice('ABCDEFG', 2, 4) --> C D # islice('ABCDEFG', 2, None) --> C D E F G # islice('ABCDEFG', 0, None, 2) --> A C E G s = slice(*args) it = iter(range(s.start or 0, s.stop or sys.maxsize, s.step or 1)) try: nexti = next(it) #得到第一个序号 except StopIteration: return for i, element in enumerate(iterable): if i == nexti: yield element nexti = next(it)
|
实际用起来就是
1 2
| for i in islice(fab(),3,9): print(i)
|
尾调用
尾调用就是在return一个函数以后没有新的运算,堆栈直接把原来的函数pop掉,push新的函数,可以用来防爆栈。实际操作就是把变化的参数传递给递归函数的变量。
1 2 3 4
| def sum(n, total = 0): if n == 0: return total return sum(n - 1, total+n)
|
然而python不支持尾调用orz
sys.getrecursionlimit()
查看最大递归深度,sys.setrecursionlimit()
可以设置。
说好的1000爆栈怎么sum(971)
就爆了,最后可以的是sum(970)
,微妙。
可以用reference里面的菊苣的代码实现真*尾调用不爆栈。
Tips: c/cpp有优化所以会说是tail_call_optimize, python3可以用装饰器实现尾调用(默认栈深度1000且没有尾调用)但是没有优化,单纯的防爆栈。
Update
看到一个新的写法, 以下引用:
1 2 3 4 5 6 7
| def fib(count, cur=0, next_=1): if count <= 1: yield cur else: # Notice that this is the tail-recursive version # of fib. yield fib(count-1, next_, cur + next_)
|
那我们怎么办呢,这里我们使用的技术称为Trampline
,我们用一个方法让它不断执行生成器的next(),达到我们的目的。
1 2 3 4 5 6 7 8
| import types def tramp(gen, *args, **kwargs): g = gen(*args, **kwargs) while isinstance(g, types.GeneratorType): g=g.__next__() return g print tramp(fib, 996)
|
fib的返回也是一个生成器,所以要算fib的第1000项的话,相当于把这些生成器都跑一遍(生成器里返回生成器感觉好奇妙)。
用ipython的%timeit比了一下,之前的方法输惨了啊(瓶颈在exception捕获?)
1 2 3 4 5
| %timeit -n 1000 tramp(fib1, 1000) 1000 loops, best of 3: 817 µs per loop %timeit -n 1000 fib(1000) 1000 loops, best of 3: 2.78 ms per loop
|
总之超帅的
Reference:
两篇写的都real棒