2015-09-24 16 views
8

Tôi muốn biết liệu chuỗi được tạo có ít hơn 2 mục nhập hay không.Làm thế nào để biết một chuỗi được tạo ra có chiều dài nhất định là

>>> def sequence(): 
...  for i in xrange(secret): 
...   yield i 

phương pháp kém hiệu quả của tôi là tạo ra một danh sách, và đo chiều dài của nó:

>>> secret = 5 
>>> len(list(sequence())) < 2 
True 

Rõ ràng, điều này tiêu thụ toàn bộ máy phát điện.

Trong trường hợp thực sự của tôi, trình tạo có thể đang đi qua một mạng lớn. Tôi muốn làm kiểm tra mà không cần tiêu thụ toàn bộ máy phát điện, hoặc xây dựng một danh sách lớn.

Có một recipe in the itertools documentation:

def take(n, iterable): 
    "Return first n items of the iterable as a list" 
    return list(islice(iterable, n)) 

này chỉ xây dựng một danh sách dài tối đa n, mà là tốt hơn.

Vì vậy, tôi có thể nói:

>>> len(take(2, sequence()) < 2 

Có một cách thậm chí pythonic hiệu quả hơn để làm điều đó?

+0

Đây là một câu hỏi lớn Peter Gỗ - cách tiếp cận ngây thơ của tôi sẽ được tạo ra một dự kiến ​​hai và lưu trữ chúng, nhưng phải có cách tốt hơn. Tôi tò mò về nó. Tôi nghi ngờ một máy phát điện có thể biết kích thước của nó trước khi được tiêu thụ. –

+0

Silghtly không liên quan nhưng như quy tắc chung hãy cẩn thận với 'danh sách (iterator)', vòng lặp có thể là vô hạn ... –

+1

Chắc chắn câu hỏi quan trọng là - làm thế nào bạn sẽ biết, * mà không * tiêu thụ iterator, nếu nó sẽ là đủ dài (hoặc đủ ngắn? Tiêu đề của bạn dường như xung đột với câu hỏi)? Bạn có bất kỳ thông tin nào khác có thể cho phép bạn xác định điều này không? – jonrsharpe

Trả lời

0

Các giải pháp sử dụng take sử dụng islice, xây dựng một danh sách và lấy chiều dài của nó:

>>> from itertools import islice 
>>> len(list(islice(sequence(), 2)) 
2 

Để tránh tạo ra danh sách chúng ta có thể sử dụng sum:

>>> sum(1 for _ in islice(sequence(), 2) 
2 

này mất khoảng 70% thời gian:

>>> timeit('len(list(islice(xrange(1000), 2)))', 'from itertools import islice') 
1.089650974650752 

>>> timeit('sum(1 for _ in islice(xrange(1000), 2))', 'from itertools import islice') 
0.7579448552500647 

Bao bì nó lên:

>>> def at_most(n, elements): 
...  return sum(1 for _ in islice(elements, n + 1)) <= n 

>>> at_most(5, xrange(5)) 
True 

>>> at_most(2, xrange(5)) 
False 
7

Kể từ Python 3.4, máy phát có thể triển khai length hint. Nếu một máy phát điện thực hiện điều này, nó sẽ được hiển thị thông qua object.__length_hint__() method.

Bạn có thể kiểm tra với số operator.length_hint() function.

Nếu đó là không sẵn, lựa chọn duy nhất của bạn là để tiêu thụ yếu tố, và việc bạn sử dụng take() công thức là cách hiệu quả nhất để làm điều đó:

from operator import length_hint 
from itertools import chain 

elements = [] 
length = length_hint(gen, None) 
if length is None: 
    elements = list(take(2, gen)) 
    length = len(elements) 
if length >= 2: 
    # raise an error 
# use elements, then gen 
gen = chain(elements, gen) 
+0

Rất thú vị, cảm ơn bạn - Tôi tò mò về các hoạt động bên trong của __length_hint __() bây giờ ... :) –

+0

@ReblochonMasque: nhiều máy phát có thể tính toán trước hoặc ít nhất là ước tính. Nó phụ thuộc hoàn toàn vào máy phát điện có liên quan, mặc dù. –

+0

Tôi không biết điều đó, cảm ơn bạn. –

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