8

我寫了一個簡單的腳本來解決一個「邏輯謎題」,這是一種來自學校的謎題類型,您可以在其中獲得許多規則,然後必須能夠找到問題的解決方案,例如「有五個音樂家名爲A,B ,C,D和E在一場音樂會中演奏,每一個都依次演奏......如果A在B之前,而D不是最後......誰在什麼時候演奏?等是否有Python語言用於評估短路的函數/表達式列表?

評估可能的解決方案,我寫的每一個「規則」作爲一個獨立的功能,如果可能的解決方案(簡單地表示爲一個字符串列表)是有效的,其將評估,例如

#Fifth slot must be B or D 
def rule1(solution): 
    return solution[4] == 'B' or solution[4] == 'D' 

#There must be at least two spots between A and B 
def rule2(solution): 
    returns abs(solution.index('A') - solution.index('B')) >= 2 

#etc... 

我對尋找Pythonic方法感興趣,以測試一個可能的解決方案是否通過了所有這些規則,並能夠在第一個失敗後停止評估規則。

起初,我寫了簡單的方式就是:

def is_valid(solution): 
    return rule1(solution) and rule2(solution) and rule3(solution) and ... 

但這似乎相當難看。我想也許我能做出這樣讀的東西,如一個列表理解多一點優雅......

def is_valid(solution) 
    rules = [rule1, rule2, rule3, rule4, ... ] 
    return all([r(solution) for f in rules]) 

...但後來我意識到,自從all()功能之前生成的列表中理解評估,即這具有根本不會短路的副作用 - 即使第一個返回False,也將評估每個規則。

所以我的問題是:是否有一個更Python /功能性的方式才能夠評估True/False表情列表,以及短路,而不需要寫出來的return f1(s) and f2(s) and f3(s) ...一個長長的清單?

回答

13

使用generator expression

rules = [ rule1, rule2, rule3, rule4, ... ] 
rules_generator = (r(solution) for r in rules) 
return all(rules_generator) 

語法糖:你可以省略額外的括號:

rules = [ rule1, rule2, rule3, rule4, ... ] 
return all(r(solution) for r in rules) 

發電機是(基本上)與.next()方法的對象,它返回的下一個項目在一些迭代中。這意味着他們可以做一些有用的事情,例如以塊的形式讀取文件,而不將其全部加載到內存中,或者迭代到大整數。你可以用for循環透明地遍歷它們; Python在幕後處理它。例如,range是Py3k中的一個生成器。

您可以通過使用yield聲明,而不是return在函數定義滾你自己的自定義生成器表達式:

def integers(): 
    i = 0 
    while True: 
     yield i 

和Python將處理保存功能的狀態等。他們真棒!

+1

所以這裏的基本區別是省略了'return all([r(solution)for r in rules])'中的括號,因此在'all()'被評估之前不會創建所有結果的列表? – 2010-08-04 13:16:10

+2

是的。在這兩種情況下,「all」的參數在傳遞之前進行評估,但評估列表理解會在內存中創建整個列表,而評估生成器表達式會創建一個生成器對象,該對象根據需要加載元素。 – katrielalex 2010-08-04 13:19:21

+0

完美,這使得很多感 - 感謝 – 2010-08-04 13:23:47