2017-04-03 34 views
3

聲明我有一個下列用例:捕獲方法,如果在列表理解

[(x, f(x)) for x in list_x if f(x) == cond(x)] 

上面所列內容的理解,我覺得使得F(X)調用兩次?我該如何避免並捕獲f(x)的值,以便f(x)只被調用一次。

我想,簡單的解決辦法是將上面的列表理解轉換爲for循環,但我好奇,如果它可以使用列表理解有效地完成。

回答

6

您可以使用嵌套生成器表達式來調用該函數只有一次:

[(x, fx) for (x, fx) in ((x, f(x)) for x in list_x) if fx == cond(x)] 

生成器表達式以鎖步形式迭代以生成列表理解的元組(x, fx)

如果您發現讀者容易些,你可以打出發電機表達到一個單獨的名頭:

mapped_x = ((x, f(x)) for x in list_x) 
filtered_x = [(x, fx) for (x, fx) in mapped_x if fx == cond(x)] 

再次重申了這一點:發電機表達式執行懶洋洋的;表達式中的for循環對於for ... in mapped_x循環中的每個步驟都是逐步進階的。

演示:

>>> list_x = range(5) 
>>> f = lambda x: print('f({!r})'.format(x)) or (x ** 2 - 1) 
>>> cond = lambda x: print('cond({!r})'.format(x)) or x % 2 == 0 
>>> mapped_x = ((x, f(x)) for x in list_x) 
>>> [(x, fx) for (x, fx) in mapped_x if fx == cond(x)] 
f(0) 
cond(0) 
f(1) 
cond(1) 
f(2) 
cond(2) 
f(3) 
cond(3) 
f(4) 
cond(4) 
[(1, 0)] 

f(x)是如何調用只有一次,條件是立即檢查。

這是多高效取決於呼叫的代價是多少;生成器表達式作爲單獨的功能框架執行,並且解釋器將在兩個框架之間切換(用於列表理解的循環也是框架對象)。

如果f(x)是一個Python函數,你已經贏了,因爲你現在減半創建的功能框對象的數量(每個f(x)調用創建一個框架對象太多,而創建這些成本相對較高)。對於C函數,您應該使用timeit module創建一些試運行,以查看預期列表大小的更快速度。

1

如果你不想叫f兩次,則可以申請f到列表第一

[a for a in map(lambda x: (x, f(x)), list_x) if a[1] == cond(x)]

+0

如果你使用'map'爲什麼不使用'filter'呢?... – Julien

+0

歡迎您提供您的答案 –

+0

注意版本,使用'lambda'顯著減慢下來(創建和彈出對於列表長度N,功能框架對象N次)。 –

1

過濾器將是一個乾淨的方式來做到這一點。它會評估傳遞的函數,如果它返回true,它將保留該元素,否則,不。

print (list(filter(lambda x: x[1] == cond(x[0]), [(x, f(x)) for x in list_x])))