2012-06-22 37 views
6

viết một hàm chung có thể lặp qua bất kỳ vòng lặp nào có thể lặp lại hiện tại, cặp tiếp theo.python ngay bây giờ, tiếp theo, n lần lặp

def now_nxt(iterable): 
    iterator = iter(iterable) 
    nxt = iterator.__next__() 
    for x in iterator: 
     now = nxt 
     nxt = x 
     yield (now,nxt) 

for i in now_nxt("hello world"): 
    print(i) 

('h', 'e') 
('e', 'l') 
('l', 'l') 
('l', 'o') 
('o', ' ') 
(' ', 'w') 
('w', 'o') 
('o', 'r') 
('r', 'l') 
('l', 'd') 

Tôi đã suy nghĩ về cách tốt nhất để viết một hàm có thể đặt số lượng mục trong mỗi bộ.

ví dụ nếu nó là

func("hello",n=3) 

kết quả sẽ là:

('h','e','l') 
('e','l','l') 
('l','l','o') 

Tôi mới vào sử dụng timeit, vì vậy hãy chỉ ra nếu tôi làm điều gì sai trái ở đây:

import timeit 

def n1(iterable, n=1): 
    #now_nxt_deque 
    from collections import deque 
    deq = deque(maxlen=n) 
    for i in iterable: 
     deq.append(i) 
     if len(deq) == n: 
      yield tuple(deq) 

def n2(sequence, n=2): 
    # now_next 
    from itertools import tee 
    iterators = tee(iter(sequence), n) 
    for i, iterator in enumerate(iterators): 
     for j in range(i): 
      iterator.__next__() 
    return zip(*iterators) 

def n3(gen, n=2): 
    from itertools import tee, islice 
    gens = tee(gen, n) 
    gens = list(gens) 
    for i, gen in enumerate(gens): 
     gens[i] = islice(gens[i], i, None) 
    return zip(*gens) 


def prin(func): 
    for x in func: 
     yield x 

string = "Lorem ipsum tellivizzle for sure ghetto, consectetuer adipiscing elit." 

print("func 1: %f" %timeit.Timer("prin(n1(string, 5))", "from __main__ import n1, string, prin").timeit(100000)) 
print("func 2: %f" %timeit.Timer("prin(n2(string, 5))", "from __main__ import n2, string, prin").timeit(100000)) 
print("func 3: %f" %timeit.Timer("prin(n3(string, 5))", "from __main__ import n3, string, prin").timeit(100000)) 

kết quả:

$ py time_this_function.py 
func 1: 0.163129 
func 2: 2.383288 
func 3: 1.908363 
+0

Có lẽ không :) –

+0

Trông tốt với tôi. Tôi có thể thử loại bỏ len() kiểm tra bằng cách có hai vòng: một để thủ deque với các mục n-1 đầu tiên, và sau đó một vòng lặp để sản lượng đầy đủ. Nhưng tôi cũng có thể quyết định nó tốt hơn chỉ với một vòng lặp. –

+1

Bạn có thể muốn xem xét chỉ cần đặt câu hỏi "cách thực hiện việc này" và sau đó đăng câu hỏi của bạn dưới dạng câu trả lời, thay vì đặt câu hỏi đó vào câu hỏi. –

Trả lời

5

đề nghị của tôi sẽ là,

from collections import deque 

def now_nxt_deque(iterable, n=1): 
    deq = deque(maxlen=n) 
    for i in iterable: 
     deq.append(i) 
     if len(deq) == n: 
      yield tuple(deq) 

for i in now_nxt_deque("hello world", 3): 
    print(i) 

('h', 'e', 'l') 
('e', 'l', 'l') 
('l', 'l', 'o') 
('l', 'o', ' ') 
('o', ' ', 'w') 
(' ', 'w', 'o') 
('w', 'o', 'r') 
('o', 'r', 'l') 
('r', 'l', 'd') 
+0

+1: Đây là giải pháp cực kỳ hiệu quả !! – jathanism

2

Giải pháp của tôi:

def nn(itr, n): 
    iterable = iter(itr) 

    last = tuple(next(iterable, None) for _ in xrange(n)) 
    yield last 
    for _ in xrange(len(itr)): 
     last = tuple(chain(last[1:], [next(iterable)])) 
     yield last 

này đã được thực hiện cho Python 2, nếu bạn muốn sử dụng nó với Python 3, thay thế xrange với range.

next, có default tham số vĩ đại, mà sẽ được trả lại thay vì nuôi một StopIteration, bạn cũng có thể thêm này mặc định tham số để hàm của bạn như sau:

def nn(itr, n, default=None): 
    iterable = iter(itr) 

    last = tuple(next(iterable, default) for _ in xrange(n)) 
    yield last 
    for _ in xrange(len(itr)): 
     last = tuple(chain(last[1:], [next(iterable, default)])) 
     yield last 

tôi đã chơi một số chi tiết với nó, ví dụ sử dụng itr.__class__() làm mặc định, nhưng điều đó có vẻ sai đối với danh sách và bộ dữ liệu, điều đó cũng có ý nghĩa đối với các chuỗi.

+0

Đây chắc chắn là một giải pháp nhỏ gọn. –

+1

'len (itr)' không hoạt động đối với máy phát điện mà làm cho mục đích này ít chung chung hơn. –

+0

Bạn nói đúng, vì vậy tôi đã cố gắng tìm một cách tốt hơn và tôi nhớ tài liệu về itertools, tôi đã chỉnh sửa bài đăng. – dav1d

5

Đây là một cách thực sự đơn giản để làm điều đó:

  • Clone iterator của bạn n lần sử dụng sử dụng itertools.tee
  • Tạm ứng các iterator thứ ii lần
  • izip họ tất cả cùng nhau
import itertools 

def now_next(sequence, n=2): 
    iterators = itertools.tee(iter(sequence), n) 
    for i, iterator in enumerate(iterators): 
     for j in range(i): 
      iterator.next() 
    return itertools.izip(*iterators) 
+0

Giải pháp tuyệt vời! Chỉ một suy nghĩ: Vì bạn đã bắt đầu với các trình vòng lặp, có lẽ nó sẽ hợp lý khi gắn bó với mẫu đó và 'trả về itertools.izip (* vòng lặp)' ở cuối? – jathanism

+0

@ Jonathan: điểm tốt. Tôi chỉ chấp nhận với 'zip' cho sự rõ ràng. Đã chỉnh sửa. – Eric

+0

@Eric, phương pháp này có thể mở rộng như thế nào? có nhiều chi phí liên quan đến nhân bản lặp không? – beoliver

1

Một biến thể của kỹ thuật của Eric có sử dụng slicing

from itertools import tee, islice, izip 

def now_next(gen, n=2): 
    gens = tee(gen, n) 
    gens = list(gens) 
    for i, gen in enumerate(gens): 
    gens[i] = islice(gens[i], i, None) 
    return izip(*gens) 

for x in now_next((1,2,3,4,5,6,7)): 
    print x 
+0

điều này dường như nhanh hơn Erics, tôi đã thêm hàm vào danh sách thời gian chờ trong câu hỏi. - lưu ý rằng khi tôi đang sử dụng Python 3.X, izip bây giờ là zip – beoliver

+0

[Tôi có thể thực hiện điều đó xuống một dòng] (http://stackoverflow.com/a/11167811/102441) – Eric

0

Một one-liner dựa trên câu trả lời của cravoori:

from itertools import tee, islice, izip 

def now_next(gen, n=2): 
    return izip(*(islice(g, i, None) for i, g in enumerate(tee(gen, n)))) 
Các vấn đề liên quan