2017-07-17 188 views
0

当我读到另一个关于finding all cycles in graph implementation的讨论时,我遇到了这个问题。任何人都可以请解释在这个例子中使用这对关键字?谢谢。在Python中,这对关键字“continue”和“yield”是做什么的?

01 def dfs(graph, start, end): 
02  fringe = [(start, [])] 
03  while fringe: 
04   state, path = fringe.pop() 
05   if path and state == end: 
06    yield path 
07    continue 
08   for next_state in graph[state]: 
09    if next_state in path: 
10     continue 
11    fringe.append((next_state, path+[next_state])) 

>>> graph = { 1: [2, 3, 5], 2: [1], 3: [1], 4: [2], 5: [2] } 
>>> cycles = [[node]+path for node in graph for path in dfs(graph, node, node)] 
>>> len(cycles) 
7 
>>> cycles 
[[1, 5, 2, 1], [1, 3, 1], [1, 2, 1], [2, 1, 5, 2], [2, 1, 2], [3, 1, 3], [5, 2, 1, 5]] 

回答

1

这两个关键字并不紧密相关。

continue关键字只能出现在一个循环(一forwhile语句)的本体,并且使得控制流返回回路而不是继续尽管环体的其余部分的顶部。在ifelse块中缩进循环体的整个其余部分通常是一种替代方法。这:

完全等价的:

while foo(): 
    if not something(): 
     bar() 
     baz() # but note that these lines are more indented in this version! 

密切相关continue另一个关键词是break,这将导致控制流立即退出循环,而不是打算重回巅峰。 continuebreak只能影响最接近的循环,所以如果你有嵌套的控制结构,它可能很难一次全部脱离它们(或者从外部循环内部的外部循环)。

yield关键字是相当不同的。尽管它经常出现在循环中,但并不一定。相反,它只能在函数的主体中被允许。它将功能更改为“发电机功能”。当调用生成器函数时,其代码不会立即运行,而是会创建一个“生成器对象”并返回给调用者。生成器对象是一种迭代器,可以通过for循环迭代(或通过调用next()手动迭代)。只有当生成器对象被迭代时,函数的代码才会运行。每次达到yield语句时,该函数的执行都将暂停,并将生成的值(或None,如果没有指定值)将作为迭代值给出。 (请注意,当有人随口叫的东西“发电机”,它们可能意味着无论是发电机的功能或发电机对象。它通常是明确的,他们根据上下文的意思。)

这是一个使用发电机来打印1一些示例代码,23

def generator_function(): 
    yield 1 # because it contains `yield` statements, this is a generator function 
    yield 2 
    yield 3 

generator_object = generator_function() # you can create a variable for the generator object 
for value in generator_object: # but often you'd create it on the same line as the loop 
    print(value) 

另一个关键字有点类似于yieldreturn,这也仅是有意义的函数。它立即结束函数的执行,返回指定的值(如果没有指定值,则返回None)。

您显示的dfs函数一个接一个地使用yieldcontinue。这样做首先产生一个值(停止生成器函数的执行,直到请求下一个值),然后一旦执行恢复,它就返回到循环的开始。

如果你愿意,你可以重写功能,避免这类原因(尽管得到的功能会稍有不同,因为它不再是一个懒惰的发电机):

def dfs(graph, start, end): 
    results = [] # maintain a list of results to avoid yielding 
    fringe = [(start, [])] 
    while fringe: 
     state, path = fringe.pop() 
     if path and state == end: 
      results.add(path) # don't yield any more, just add the path to the results list 
     else: # add an else block instead of using continue 
      for next_state in graph[state]: 
       if next_state not in path: # reverse the condition instead of continue 
        fringe.append((next_state, path+[next_state])) 
    return results # return the results at the end of the function 

我会注意,在大多数情况下,函数的生成器版本可能会更好。使用continue而不是更多的缩进是更多的样式选择,并且对代码的逻辑或性能(仅仅是它的外观)没有太大影响。