62

I have a list comprehension which approximates to:

[f(x) for x in l if f(x)]

Where l is a list and f(x) is an expensive function which returns a list.

I want to avoid evaluating f(x) twice for every non-empty occurance of f(x). Is there some way to save its output within the list comprehension?

I could remove the final condition, generate the whole list and then prune it, but that seems wasteful.

Edit:

Two basic approaches have been suggested:

An inner generator comprehension:

[y for y in (f(x) for x in l) if y]

or memoization.

I think the inner generator comprehension is elegant for the problem as stated. In actual fact I simplified the question to make it clear, I really want:

[g(x, f(x)) for x in l if f(x)]

For this more complicated situation, I think memoization produces a cleaner end result.

4

12 に答える 12

9
[y for y in [f(x) for x in l] if y]

For your updated problem, this might be useful:

[g(x,y) for x in l for y in [f(x)] if y]
于 2013-04-04T13:34:54.367 に答える
8

いいえ。これを行う(クリーンな) 方法はありません。古き良きループに問題はありません。

output = []
for x in l:
    result = f(x)
    if result: 
        output.append(result)

読みにくい場合は、いつでも関数でラップできます。

于 2013-04-04T13:34:02.050 に答える
4

メモ化に関して多くの回答がありました。Python 3 標準ライブラリlru_cacheには、最近使用した最後のキャッシュである が含まれるようになりました。だからあなたはできる:

from functools import lru_cache

@lru_cache()
def f(x):
    # function body here

このようにして、関数は一度だけ呼び出されます。のサイズを指定することもできます。lru_cacheデフォルトでは、これは 128 です。上記の memoize デコレーターの問題は、リストのサイズが手に負えないほど大きくなる可能性があることです。

于 2014-09-10T03:56:45.073 に答える
1

これが私の解決策です:

filter(None, [f(x) for x in l])
于 2013-04-04T14:17:01.427 に答える
-2

定義はどうですか:

def truths(L):
    """Return the elements of L that test true"""
    return [x for x in L if x]

そのため、たとえば

> [wife.children for wife in henry8.wives]
[[Mary1], [Elizabeth1], [Edward6], [], [], []]

> truths(wife.children for wife in henry8.wives) 
[[Mary1], [Elizabeth1], [Edward6]]
于 2013-04-04T19:22:31.327 に答える