2011-12-16 36 views
110

Câu hỏi ngu ngốc phía trước: Tôi muốn một cách thành ngữ để tìm phần tử đầu tiên trong danh sách khớp với vị từ.tìm phần tử đầu tiên trong một chuỗi khớp với vị từ

Mã hiện nay là khá xấu xí:

[x for x in seq if predicate(x)][0] 

Tôi đã suy nghĩ về việc thay đổi nó để:

from itertools import dropwhile 
dropwhile(lambda x: not predicate(x), seq).next() 

Nhưng phải có một cái gì đó tao nhã hơn ... Và nó sẽ được tốt đẹp nếu nó trả về giá trị None thay vì tăng ngoại lệ nếu không tìm thấy kết quả phù hợp.

Tôi biết tôi chỉ có thể xác định một chức năng như:

def get_first(predicate, seq): 
    for i in seq: 
     if predicate(i): return i 
    return None 

Nhưng nó là khá nhạt để bắt đầu điền mã với chức năng tiện ích như thế này (và những người có thể sẽ không nhận thấy rằng họ đã có, vì vậy chúng có xu hướng được lặp đi lặp lại theo thời gian) nếu có các bản dựng sẵn được cung cấp giống nhau.

+1

Đây không phải là một câu hỏi ngu ngốc, và @ j-f-sebastian: đây không phải là một bản sao. Câu hỏi này đặc biệt về việc trả về một đối tượng và trả về 'Không có' thay vì ném một ngoại lệ. Nó cũng về sự thanh lịch. Câu hỏi còn lại là câu hỏi n00b và không hỏi những điều này, ít nhất là không rõ ràng lắm. –

+1

Ngoài việc được hỏi muộn hơn "[chức năng tìm chuỗi python] (https://stackoverflow.com/questions/6039425/python-sequence-find-function)", câu hỏi này có tiêu đề ** tốt hơn **. – Wolf

Trả lời

159

next(x for x in seq if predicate(x))

Nó làm tăng StopIteration nếu có ai sánh kịp.

next(ifilter(predicate, seq), None)

lợi nhuận None nếu không có yếu tố như vậy.

+12

Hoặc bạn có thể cung cấp đối số "mặc định" thứ hai cho 'next' được sử dụng thay vì tăng ngoại lệ. –

+2

@fortran: ['next()'] (http://docs.python.org/library/functions.html#next) có sẵn từ Python 2.6 Bạn có thể đọc [Trang mới] (http: // tài liệu. python.org/whatsnew/2.7.html) để nhanh chóng tự làm quen với các tính năng mới. – jfs

+1

Tôi là một newbie python và đọc tài liệu và ifilter sử dụng phương thức "yield". Tôi cho rằng điều này có nghĩa là vị ngữ được đánh giá uể oải khi chúng ta đi. tức là, chúng tôi không chạy biến vị ngữ thông qua toàn bộ danh sách vì tôi có hàm vị ngữ hơi đắt tiền và tôi chỉ muốn lặp lại cho đến khi chúng ta tìm thấy một mục –

68

Bạn có thể sử dụng một biểu thức máy phát điện với giá trị mặc định và sau đó next nó:

next((x for x in seq if predicate(x)), None) 

Mặc dù cho điều này một lót bạn cần phải sử dụng Python> = 2,6.

Bài viết khá phổ biến này thảo luận thêm về vấn đề này: Cleanest Python find-in-list function?.

3

Tôi không nghĩ có bất kỳ điều gì sai với giải pháp mà bạn đã đề xuất trong câu hỏi của mình.

Trong mã của riêng tôi, tôi sẽ thực hiện nó như mặc dù điều này:

(x for x in seq if predicate(x)).next() 

Cú pháp với () tạo ra một máy phát điện, đó là hiệu quả hơn so với việc tạo ra tất cả các danh sách cùng một lúc với [].

+0

Và không chỉ vậy - với '[]' bạn có thể gặp phải vấn đề nếu trình lặp không bao giờ kết thúc hoặc các phần tử của nó khó tạo, sau đó nó được ... – glglgl

+5

đối tượng ''generator' không có thuộc tính 'next'' trên Python 3. – jfs

+0

@glglgl - Đối với điểm đầu tiên (không bao giờ kết thúc) tôi nghi ngờ nó, vì đối số là một chuỗi hữu hạn [chính xác hơn một danh sách theo câu hỏi của OP]. Đối với thứ hai: một lần nữa, kể từ khi đối số được cung cấp là một chuỗi, các đối tượng nên đã được tạo và lưu trữ bởi thời gian chức năng này được gọi là .... hoặc tôi thiếu một cái gì đó? – mac

1

J.F. Câu trả lời của Sebastian là thanh lịch nhất nhưng yêu cầu python 2.6 như fortran chỉ ra.

Đối với phiên bản Python < 2.6, đây là tốt nhất mà tôi có thể đưa ra:

from itertools import repeat,ifilter,chain 
chain(ifilter(predicate,seq),repeat(None)).next() 

Hoặc nếu bạn cần một danh sách sau (danh sách xử lý các StopIteration), hoặc bạn cần nhiều hơn chỉ là người đầu tiên nhưng vẫn không phải tất cả, bạn có thể làm điều đó với islice:

from itertools import islice,ifilter 
list(islice(ifilter(predicate,seq),1)) 

UPDATE: Mặc dù tôi cá nhân sử dụng một chức năng được xác định trước gọi là đầu tiên() mà bắt một StopIteration và trả về Không, đây là một sự cải thiện tốt hơn ví dụ trên: tránh sử dụng bộ lọc/IFilter:

from itertools import islice,chain 
chain((x for x in seq if predicate(x)),repeat(None)).next() 
+8

Yikes! nếu nó đi xuống đến đó, tôi sẽ chỉ làm đơn giản "cho" vòng lặp với một "nếu" bên trong nó - dễ dàng hơn nhiều để đọc –

Các vấn đề liên quan