2012-01-06 32 views
7

Tôi muốn xây dựng một giá trị trong một danh sách hiểu, nhưng cũng lọc trên giá trị đó. Ví dụ:Có thể nắm bắt giá trị trả về từ một danh sách hiểu Python để sử dụng điều kiện không?

[expensive_function(x) for x in generator where expensive_function(x) < 5] 

Tôi muốn tránh gọi expensive_function hai lần mỗi lần lặp lại.

generator có thể trả về chuỗi vô hạn và danh sách không được đánh giá một cách lười biếng. Vì vậy, điều này sẽ không hoạt động:

[y in [expensive_function(x) for x in generator where expensive_function(x)] where y < 5] 

Tôi có thể viết theo cách khác, nhưng cảm thấy thích hợp cho việc hiểu danh sách và tôi chắc chắn đây là mẫu sử dụng phổ biến (có thể hay không!).

+0

Có bất kỳ câu trả lời nào đáng được chấp nhận không? Nếu không, bạn vẫn đang tìm kiếm thông tin gì? –

+0

Xin lỗi quên đánh dấu phần này. Cảm ơn bạn vì câu trả lời! – Joe

+0

Không sao cả. Đã hy vọng bạn chỉ cần một lời nhắc nhở. :) –

Trả lời

10

Nếu generator có thể là vô hạn, bạn không muốn sử dụng tính năng hiểu danh sách. Và không phải mọi thứ phải là một lớp lót.

def filtered_gen(gen): 
    for item in gen: 
     result = expensive_function(item) 
     if result < 5: 
      yield result 
+2

Nếu hai lần xuất hiện cuối cùng của 'mục' được thay thế bằng' kết quả'? – Chris

+0

@Chris: Vâng, cảm ơn. –

+0

+1 cho điều này. Bạn có thể sử dụng các biểu thức của trình soạn thảo và trình phát, nhưng điều này dễ hiểu hơn nhiều. –

2

bạn nên thực hiện biểu thức 2 máy phát điện:

ys_all = (expensive(x) for x in xs) 
ys_filtered = (y for y in ys_all if y <5) 

hoặc

from itertools import imap, ifilter 
ys = ifilter(lambda y : y < 5, imap(expensive, xs)) 
+0

Không. Không nếu 'xs' là vô hạn. Đáng buồn là Python không có tính năng hiểu danh sách Haskell. Một cái gì đó, một nơi nào đó, sẽ nổ tung. – Joe

+0

câu trả lời được cập nhật với trình lặp số – Simon

+0

Có! Đây là câu trả lời hay nhất theo ý kiến ​​của tôi! Có thể bạn có thể gọi chúng là "sự hiểu biết máy phát điện" thay vì việc hiểu danh sách? –

1

Warning Đây là một chút phức tạp nhưng không được công việc. Tôi sẽ sử dụng một ví dụ để giải thích nó.

Hãy nói expensive_function = math.sin

infinite generator = collections.count(0.1,0.1)

sau đó

[z for z in (y if y < 5 else next(iter([])) 
    for y in (math.sin(x) for x in itertools.count(0.1,0.1)))] 

[0.09983341664682815, 
0.19866933079506122, 
0.2955202066613396, 
0.3894183423086505, 
0.479425538604203] 

Vì vậy, vấn đề của bạn nắm để

[z for z in (y if y < 0.5 else next(iter([])) \ 
     for y in (expensive_function(x) for x in generator))] 

Bí quyết là để buộc một StopIteration từ một máy phát điện và không có gì thanh lịch hơn next(iter([]))

Đây expensive_function chỉ được gọi một lần mỗi lần lặp.

Mở rộng Máy phát vô hạn bằng Máy phát điện hữu hạn, với Điều kiện Dừng. Khi trình tạo không cho phép raise StopIteration, chúng tôi sẽ chọn một cách phức tạp tức lànext(iter([])) Và bây giờ bạn có một máy phát điện hữu hạn, có thể được sử dụng trong một danh sách Hiểu

Như OP đã quan tâm đến việc áp dụng các phương pháp trên cho một phi monotonic chức năng đây là một chức năng không đơn điệu hư cấu

đắt Non-Monotonic Chức năng f(x) = random.randint(1,100)*x

Dừng Điều kiện = < 7

[z for z in (y if y < 7 else next(iter([])) for y in 
     (random.randint(1,10)*x for x in itertools.count(0.1,0.1)))] 

[0.9, 
0.6000000000000001, 
1.8000000000000003, 
4.0, 
0.5, 
6.0, 
4.8999999999999995, 
3.1999999999999997, 
3.5999999999999996, 
5.999999999999999] 

Btw: 01.theo nghĩa thực là không đơn điệu trên toàn bộ phạm vi (0,2pi)

+0

Điên! Điều này là khác biệt một cách tinh tế, bởi vì 'sin' là một hàm không đơn điệu (tôi không nói rằng' hàm đắt tiền_function' là đơn điệu, nhưng nó là!) Và điều này dừng lại ** lần đầu tiên ** điều kiện không đúng, không tiếp tục ** cho tất cả các điều kiện ** trong trường hợp điều kiện là đúng. Điều đó nói rằng, nếu nó không đơn điệu, điều này sẽ dẫn đến một đánh giá vô hạn ... – Joe

+0

@Joe, Điều này sẽ làm việc ngay cả đối với chức năng không đơn điệu. Xem cập nhật của tôi – Abhijit

2

Tôi sẽ trả lời một phần câu hỏi về cách nắm bắt kết quả trung gian trong danh sách hiểu để sử dụng trong điều kiện và bỏ qua câu hỏi của danh sách hiểu được xây dựng từ một máy phát điện vô hạn (mà rõ ràng là không phải là đi làm việc), chỉ trong trường hợp bất cứ ai tìm kiếm một câu trả lời cho câu hỏi trong tiêu đề đến đây.

Vì vậy, bạn có một danh sách hiểu như thế này:

[expensive_function(x) for x in xrange(5) if expensive_function(x) % 2 == 0] 

Và bạn muốn tránh tính expensive_function hai lần khi nó đi bộ lọc của bạn. Ngôn ngữ với cú pháp hiểu biểu cảm hơn (Scala, Haskell, vv) cho phép bạn chỉ đơn giản là gán tên cho biểu thức tính toán từ biến sự hiểu biết, cho phép bạn làm những việc như sau:

# NOT REAL PYTHON 
[result for x in xrange(5) for result = expensive_function(x) if result % 2 == 0] 

Nhưng bạn có thể dễ dàng bắt chước này bằng cách chuyển sự phân công result = expensive_function(x) vào một for lặp qua một chuỗi các một yếu tố:

[result for x in xrange(5) for result in (expensive_function(x),) if result % 2 == 0] 

Và bằng chứng:

>>> def expensive_function(x): 
     print 'expensive_function({})'.format(x) 
     return x + 10 
>>> [expensive_function(x) for x in xrange(5) if expensive_function(x) % 2 == 0] 
expensive_function(0) 
expensive_function(0) 
expensive_function(1) 
expensive_function(2) 
expensive_function(2) 
expensive_function(3) 
expensive_function(4) 
expensive_function(4) 
[10, 12, 14] 
>>> [result for x in xrange(5) for result in (expensive_function(x),) if result % 2 == 0] 
expensive_function(0) 
expensive_function(1) 
expensive_function(2) 
expensive_function(3) 
expensive_function(4) 
[10, 12, 14] 
Các vấn đề liên quan