說我有比其他兩個列表一個更長的時間,x = [1,2,3,4,5,6,7,8]
和y = [a,b,c]
,我想每一個元素y中合併到每3個指標在X這樣的結果列表ž看起來像:z = [1,2,a,3,4,b,5,6,c,7,8]
合併2所列出每個x位置
什麼是在python中進行此操作的最佳方式?
說我有比其他兩個列表一個更長的時間,x = [1,2,3,4,5,6,7,8]
和y = [a,b,c]
,我想每一個元素y中合併到每3個指標在X這樣的結果列表ž看起來像:z = [1,2,a,3,4,b,5,6,c,7,8]
合併2所列出每個x位置
什麼是在python中進行此操作的最佳方式?
下面是從itertools documentation的輪轉配方的改編版本,應該做你想要什麼:
from itertools import cycle, islice
def merge(a, b, pos):
"merge('ABCDEF', [1,2,3], 3) --> A B 1 C D 2 E F 3"
iterables = [iter(a)]*(pos-1) + [iter(b)]
pending = len(iterables)
nexts = cycle(iter(it).next for it in iterables)
while pending:
try:
for next in nexts:
yield next()
except StopIteration:
pending -= 1
nexts = cycle(islice(nexts, pending))
例子:
>>> list(merge(xrange(1, 9), 'abc', 3)) # note that this works for any iterable!
[1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8]
或者這裏是你如何可以使用roundrobin()
因爲它沒有任何修改:
>>> x = [1,2,3,4,5,6,7,8]
>>> y = ['a','b','c']
>>> list(roundrobin(*([iter(x)]*2 + [y])))
[1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8]
或等效的,但是稍微更可讀的版本:
>>> xiter = iter(x)
>>> list(roundrobin(xiter, xiter, y))
[1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8]
注意,這兩種方法都具有可迭代的,不僅僅是序列工作。
原來這裏是roundrobin()
實現:
from itertools import cycle, islice
def roundrobin(*iterables):
"roundrobin('ABC', 'D', 'EF') --> A D E B F C"
# Recipe credited to George Sakkis
pending = len(iterables)
nexts = cycle(iter(it).next for it in iterables)
while pending:
try:
for next in nexts:
yield next()
except StopIteration:
pending -= 1
nexts = cycle(islice(nexts, pending))
>>> from itertools import chain
def solve(x,y):
it = iter(y)
for i in xrange(0, len(x), 2):
try:
yield x[i:i+2] + [next(it)]
except StopIteration:
yield x[i:]
...
>>> x = [1,2,3,4,5,6,7,8]
>>> y = ['a','b','c']
>>> list(chain.from_iterable(solve(x,y)))
[1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8]
這種方法就地修改x
。或者,您可以製作x
的副本,如果您不想更改原件,請返回修改後的副本。過去的x
年底y
def merge(x, y, offset):
for i, element in enumerate(y, 1):
x.insert(i * offset - 1, element)
>>> x = [1,2,3,4,5,6,7,8]
>>> y = ['a','b','c']
>>> merge(x, y, 3)
>>> x
[1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8]
所有多餘的元素只是獲得追加到末尾。
上述解決方案非常酷。這是一個不涉及roundrobin或itertools的替代方案。
def merge(x, y):
result = []
while y:
for i in range(0, 2): result.append(x.pop(0))
for i in range(0, 1): result.append(y.pop(0))
result.extend(x)
return result
其中2和1是任意的,並且列表y被假定爲比列表x短。
這與我自己的實現類似,除了我使用了一個循環和一個條件。儘管我懷疑你的表現可能會更好 – KingFu
sep, lst = 2, []
for i in range(len(y)+1):
lst += x[i*sep:(i+1)*sep] + y[i:i+1]
凡sep
是y
一個元件之前的x
元件的數量被插入。
性能:
>>> timeit.timeit(stmt="for i in range(len(y)+1): lst += x[i*sep:(i+1)*sep] + y[i:i+1]", setup="lst = [];x = [1,2,3,4,5,6,7,8];y = ['a','b','c'];sep = 2", number=1000000)
2.8565280437469482
相當好。我無法獲得stmt
以let = []
開頭,所以我認爲它一直附加到lst
(除非我誤解了timeit
),但仍然......非常好的一百萬次。
使用itertools.izip_longest
:
>>> from itertools import izip_longest, chain
>>> y = ['a','b','c']
>>> x = [1,2,3,4,5,6,7,8]
>>> lis = (x[i:i+2] for i in xrange(0, len(x) ,2)) # generator expression
>>> list(chain.from_iterable([ (a + [b]) if b else a
for a, b in izip_longest(lis, y)]))
[1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8]
def merge(xs, ys):
ys = iter(ys)
for i, x in enumerate(xs, 1):
yield x
if i % 2 == 0:
yield next(ys)
''.join(merge('12345678', 'abc')) # => '12a34b56c78'
這裏的另一種方式:
x = range(1, 9)
y = list('abc')
from itertools import count, izip
from operator import itemgetter
from heapq import merge
print map(itemgetter(1), merge(enumerate(x), izip(count(1, 2), y)))
# [1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8]
這使這一切懶惰建設新名單前,並讓merge
自然合併序列...那種裝飾/未裝飾......它確實需要Python 2.7 for count
才能擁有step
參數。
所以,走它通過一個位:
a = list(enumerate(x))
# [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8)]
b = zip(count(1, 2), y)
# [(1, 'a'), (3, 'b'), (5, 'c')]
print list(merge(a, b))
# [(0, 1), (1, 2), (1, 'a'), (2, 3), (3, 4), (3, 'b'), (4, 5), (5, 6), (5, 'c'), (6, 7), (7, 8)]
然後itemgetter(1)
只需要實際值刪除索引...
我想,這是簡單明瞭,我可以理解!我無法評論任何解決方案的表現,因爲這超出了我目前的理解。 – KingFu
[插入比擴展或追加更昂貴](http://stackoverflow.com/questions/7776938/python-insert-vs-append)。 – 2rs2ts
@ 2rs2ts那麼複雜的事情呢!嗯,我可能會等待,讓更多的經驗豐富的人選擇最好的(最快?)解決方案,然後我選擇一個答案,然後 – KingFu