2012-06-22 74 views
6

編寫一個通用函數,它可以迭代現在返回的任何迭代,下一對。python now,next,n iteration

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') 

我一直在思考寫在那裏可以設置在每個元組的項目數的函數的最佳方式。

例如,如果它是

func("hello",n=3) 

的結果將是:

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

我是新來使用timeit,所以請大家指出,如果我在這裏做什麼錯事:

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)) 

結果:

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

也許不是:) –

+0

我看不錯。我可能會嘗試通過有兩個循環來擺脫len()檢查:一個是用第一個n-1項來初始化deque,然後是一個循環來產生完整的元組。但是我也可以決定只用一個循環就好了。 –

+1

您可能想考慮只是提出「如何做到這一點」的問題,然後將您的事情發佈爲答案,而不是將其放在問題中。 –

回答

5

我的建議是,

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:這是一個非常有效的解決方案! – jathanism

2

我的解決辦法:

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 

這是爲Python 2款,如果你想使用Python 3使用它,用range更換xrange

next,有很大的default參數,該參數將被退回,而不是養StopIteration,你也可以此默認參數添加到您的功能,像這樣:

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 

我打出了一些更多的它,例如使用itr.__class__()作爲默認值,但這對列表和元組來說似乎是錯誤的,對於字符串來說它是有意義的。

+0

這絕對是一個緊湊的解決方案。 –

+1

'len(itr)'不適用於使這個通用性較低的生成器。 –

+0

你是對的,所以我試圖找到一個更好的方法,我記得itertools文檔,我編輯的帖子。 – dav1d

5

這裏是一個非常簡單的方法來做到這一點:

  • 克隆你的迭代器使用使用itertools.tee
  • 推進i次迭代i
  • izip他們一起
n
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

偉大的解決方案!只有一個想法:既然你已經開始使用迭代器,也許這是堅持使用該模式,並在最後「返回itertools.izip(*迭代器)」是有意義的? – jathanism

+0

@jathanism:好點。爲了清楚起見,我只是用'zip'來尋找。編輯。 – Eric

+0

@Eric,這種方法的可擴展性如何?克隆迭代器會涉及很多成本嗎? – beoliver

1

在Eric的技術的一個變化是採用基於cravoori的回答切片

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

這似乎比Erics更快,我已經將該功能添加到問題中的timeit列表中。 - 請注意,因爲我正在使用Python 3.X,所以izip現在是zip – beoliver

+0

[我可以把它放到一行](http://stackoverflow.com/a/11167811/102441) – Eric

0

一個班輪:

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))))