問題は簡単です。リストの各要素と次の要素をペアで繰り返します(最後の要素を最初の要素でラップします)。
私はそれを行う2つの非Python的な方法について考えました:
def pairs(lst):
n = len(lst)
for i in range(n):
yield lst[i],lst[(i+1)%n]
そして:
def pairs(lst):
return Zip(lst,lst[1:]+[lst[:1]])
期待される出力:
>>> for i in pairs(range(10)):
print i
(0, 1)
(1, 2)
(2, 3)
(3, 4)
(4, 5)
(5, 6)
(6, 7)
(7, 8)
(8, 9)
(9, 0)
>>>
これを行うためのよりPythonicな方法についての提案はありますか?多分私が聞いたことがない事前定義された関数がそこにありますか?
また、より一般的なn-fold(ペアの代わりにトリプレット、カルテットなどを使用)バージョンも興味深い可能性があります。
私はタプルの一般的なバージョンを自分でコーディングしました。エレガントなシンプルさのために最初のバージョンが好きです。見れば見るほど、Pythonicに感じます...結局のところ、Zipを使用した1つのライナーよりもPythonicです。 、アスタリスク引数の展開、リスト内包表記、リストスライス、リスト連結、および「範囲」?
def ntuples(lst, n):
return Zip(*[lst[i:]+lst[:i] for i in range(n)])
Itertoolsバージョンは、大きなリストに対しても十分に効率的である必要があります...
from itertools import *
def ntuples(lst, n):
return izip(*[chain(islice(lst,i,None), islice(lst,None,i)) for i in range(n)])
また、インデックス付けできないシーケンスのバージョン:
from itertools import *
def ntuples(seq, n):
iseq = iter(seq)
curr = head = Tuple(islice(iseq, n))
for x in chain(iseq, head):
yield curr
curr = curr[1:] + (x,)
とにかく、皆さんの提案に感謝します! :-)
def pairs(lst):
i = iter(lst)
first = prev = item = i.next()
for item in i:
yield prev, item
prev = item
yield item, first
空でないシーケンスで機能し、インデックス付けは必要ありません。
私はいつものように、ティーが好きです:
from itertools import tee, izip, chain
def pairs(iterable):
a, b = tee(iterable)
return izip(a, chain(b, [next(b)]))
これで十分かもしれません:
def pairs(lst):
for i in range(1, len(lst)):
yield lst[i-1], lst[i]
yield lst[-1], lst[0]
>>> a = list(range(5))
>>> for a1, a2 in pairs(a):
... print a1, a2
...
0 1
1 2
2 3
3 4
4 0
この種のものが好きなら、python wordaligned.org の記事を見てください。著者はPythonのジェネレーターが大好きです。
私はこのようにします(主に私はこれを読むことができるので):
class Pairs(object):
def __init__(self, start):
self.i = start
def next(self):
p, p1 = self.i, self.i + 1
self.i = p1
return p, p1
def __iter__(self):
return self
if __name__ == "__main__":
x = Pairs(0)
y = 1
while y < 20:
print x.next()
y += 1
与える:
(0, 1)
(1, 2)
(2, 3)
(3, 4)
(4, 5)
(5, 6)
(6, 7)
(7, 8)
(8, 9)
[(i,(i+1)%len(range(10))) for i in range(10)]
range(10)を必要なリストに置き換えます。
一般に、Pythonでは「循環インデックス作成」は非常に簡単です。使用するだけです:
a[i%len(a)]
これは、オプションの開始インデックスをサポートするバージョンです(たとえば、最初のペアとして(4、0)を返すには、start = -1を使用します)。
import itertools
def iterrot(lst, start = 0):
if start == 0:
i = iter(lst)
Elif start > 0:
i1 = itertools.islice(lst, start, None)
i2 = itertools.islice(lst, None, start)
i = itertools.chain(i1, i2)
else:
# islice doesn't support negative slice indices so...
lenl = len(lst)
i1 = itertools.islice(lst, lenl + start, None)
i2 = itertools.islice(lst, None, lenl + start)
i = itertools.chain(i1, i2)
return i
def iterpairs(lst, start = 0):
i = iterrot(lst, start)
first = prev = i.next()
for item in i:
yield prev, item
prev = item
yield prev, first
def itertrios(lst, start = 0):
i = iterrot(lst, start)
first = prevprev = i.next()
second = prev = i.next()
for item in i:
yield prevprev, prev, item
prevprev, prev = prev, item
yield prevprev, prev, first
yield prev, first, second
一般的なケースの解決に関する質問に答えるには:
import itertools
def pair(series, n):
s = list(itertools.tee(series, n))
try:
[ s[i].next() for i in range(1, n) for j in range(i)]
except StopIteration:
pass
while True:
result = []
try:
for j, ss in enumerate(s):
result.append(ss.next())
except StopIteration:
if j == 0:
break
else:
s[j] = iter(series)
for ss in s[j:]:
result.append(ss.next())
yield result
出力は次のようになります。
>>> for a in pair(range(10), 2):
... print a
...
[0, 1]
[1, 2]
[2, 3]
[3, 4]
[4, 5]
[5, 6]
[6, 7]
[7, 8]
[8, 9]
[9, 0]
>>> for a in pair(range(10), 3):
... print a
...
[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
[4, 5, 6]
[5, 6, 7]
[6, 7, 8]
[7, 8, 9]
[8, 9, 0]
[9, 0, 1]
FortranのZip *範囲ソリューションのさらに短いバージョン(今回はラムダを使用):
group = lambda t, n: Zip(*[t[i::n] for i in range(n)])
group([1, 2, 3, 3], 2)
与える:
[(1, 2), (3, 4)]
もちろん、いつでもdequeを使用できます:
from collections import deque
from itertools import *
def pairs(lst, n=2):
itlst = iter(lst)
start = list(islice(itlst, 0, n-1))
deq = deque(start, n)
for elt in chain(itlst, start):
deq.append(elt)
yield list(deq)
_def pairs(ex_list):
for i, v in enumerate(ex_list):
if i < len(list) - 1:
print v, ex_list[i+1]
else:
print v, ex_list[0]
_
Enumerateは、インデックス番号と値を持つタプルを返します。リストの値と次の要素を出力します_ex_list[i+1]
_。 if i < len(list) - 1
は、vがnotリストの最後のメンバーであるかどうかを意味します。そうである場合:vとリストの最初の要素_print v, ex_list[0]
_を出力します。
リストを返すようにすることができます。印刷されたタプルをリストに追加して返すだけです。
_def pairs(ex_list):
result = []
for i, v in enumerate(ex_list):
if i < len(list) - 1:
result.append((v, ex_list[i+1]))
else:
result.append((v, ex_list[0]))
return result
_
これは、良いか悪いかにかかわらず、無限に循環しますが、アルゴリズム的には非常に明確です。
from itertools import tee, cycle
def nextn(iterable,n=2):
''' generator that yields a Tuple of the next n items in iterable.
This generator cycles infinitely '''
cycled = cycle(iterable)
gens = tee(cycled,n)
# advance the iterators, this is O(n^2)
for (ii,g) in Zip(xrange(n),gens):
for jj in xrange(ii):
gens[ii].next()
while True:
yield Tuple([x.next() for x in gens])
def test():
data = ((range(10),2),
(range(5),3),
(list("abcdef"),4),)
for (iterable, n) in data:
gen = nextn(iterable,n)
for j in range(len(iterable)+n):
print gen.next()
test()
与える:
(0, 1)
(1, 2)
(2, 3)
(3, 4)
(4, 5)
(5, 6)
(6, 7)
(7, 8)
(8, 9)
(9, 0)
(0, 1)
(1, 2)
(0, 1, 2)
(1, 2, 3)
(2, 3, 4)
(3, 4, 0)
(4, 0, 1)
(0, 1, 2)
(1, 2, 3)
(2, 3, 4)
('a', 'b', 'c', 'd')
('b', 'c', 'd', 'e')
('c', 'd', 'e', 'f')
('d', 'e', 'f', 'a')
('e', 'f', 'a', 'b')
('f', 'a', 'b', 'c')
('a', 'b', 'c', 'd')
('b', 'c', 'd', 'e')
('c', 'd', 'e', 'f')
('d', 'e', 'f', 'a')