雖然不知道是誰(shuí)最早發(fā)明yield來(lái)表達(dá)協(xié)程的,不過(guò)最近終于解鎖這一技能了,讓我感受到了上帝般的快感。
比方說(shuō)我們要后序遍歷二叉樹(shù),好啦我知道ACMer閉著眼睛都能默寫(xiě)出來(lái)非遞歸版本,讓我們看看遞歸版本先:
class Node(object):
def __init__(self, val, left, right):
self.val = val
self.left = left
self.right = right
def visit_post(node):
if node.left:
yield from visit_post(node.left)
if node.right:
yield from visit_post(node.right)
yield node.val
if __name__ == '__main__':
node = Node(-1, None, None)
for val in range(100):
node = Node(val, None, node)
print(list(visit_post(node)))
就是這么簡(jiǎn)單,yield from這么棒為什么你還在用Python2.x?
但是我們知道寫(xiě)成遞歸動(dòng)不動(dòng)就爆棧了,非遞歸才是正義。不過(guò)不要怕,理解yield協(xié)程之后我們?cè)僖膊槐厥謩?dòng)維護(hù)什么棧了,裝逼變得異常簡(jiǎn)單。
def visit_post(node):
if node.left:
yield node.left
if node.right:
yield node.right
yield node.val
def visit(node, visit_method):
stack = [visit_method(node)]
while stack:
last = stack[-1]
try:
yielded = next(last)
except StopIteration:
stack.pop()
else:
if isinstance(yielded, Node):
stack.append(visit_method(yielded))
elif isinstance(yielded, int):
yield yielded
if __name__ == '__main__':
node = Node(-1, None, None)
for val in range(100):
node = Node(val, None, node)
visit_generator = visit(node, visit_method=visit_post)
print(list(visit_generator))
excellent!關(guān)鍵是很好理解對(duì)不對(duì),我以前一直認(rèn)為我不把LeetCode刷7遍是不可能完成遞歸轉(zhuǎn)非遞歸的白板編程的,現(xiàn)在根本不是問(wèn)題嘛。
話說(shuō)簡(jiǎn)書(shū)的代碼高亮能不能專(zhuān)業(yè)點(diǎn),__builtins__高亮很難?這根本就是態(tài)度問(wèn)題。
或者我們有遞歸搜索二叉樹(shù)最大值什么的:
def visit_maxvalue(node):
if node.left and node.right:
return max(node.val, visit_maxvalue(node.left), visit_maxvalue(node.right))
elif node.left:
return max(node.val, visit_maxvalue(node.left))
elif node.right:
return max(node.val, visit_maxvalue(node.right))
else:
return node.val
非遞歸版本也是非常平易近人:
def visit_max_maxvalue(node):
if node.left and node.right:
yield max(node.val, (yield node.left), (yield node.right))
elif node.left:
yield max(node.val, (yield node.left))
elif node.right:
yield max(node.val, (yield node.right))
else:
yield node.val
def visit(node, visit_method):
stack = [visit_method(node)]
current = None
while stack:
last = stack[-1]
try:
yielded = last.send(current)
except StopIteration:
stack.pop()
else:
if isinstance(yielded, Node):
stack.append(visit_method(yielded))
current = None
elif isinstance(yielded, int):
current = yielded
return current
逼格飽滿有木有!
BTW,著名的Tornado就是用yield協(xié)程來(lái)實(shí)現(xiàn)異步,大愛(ài)!