Вот подход генератора. Вы, наверное, заметили, что многие из этих «списков генерации» могут быть выполнены как функции генератора. Они очень полезны: они не требуют, чтобы вы генерировали весь список перед использованием данных из него, чтобы сохранить весь список в памяти, и вы можете использовать их для непосредственного создания многих типов данных, а не только списков.
Это работает, если передан любой итератор, а не только списки.
Этот подход также проходит один из более полезных тестов: он ведет себя хорошо, когда прошел бесконечный или почти бесконечный итератор, например. linear_merge(xrange(10**9), xrange(10**9))
.
Избыточность в этих двух случаях, вероятно, может быть уменьшена, что было бы полезно, если бы вы хотели поддержать объединение более двух списков, но для ясности я здесь этого не делал.
def linear_merge(list1, list2):
"""
>>> a = [1, 3, 5, 7]
>>> b = [2, 4, 6, 8]
>>> [i for i in linear_merge(a, b)]
[1, 2, 3, 4, 5, 6, 7, 8]
>>> [i for i in linear_merge(b, a)]
[1, 2, 3, 4, 5, 6, 7, 8]
>>> a = [1, 2, 2, 3]
>>> b = [2, 2, 4, 4]
>>> [i for i in linear_merge(a, b)]
[1, 2, 2, 2, 2, 3, 4, 4]
"""
list1 = iter(list1)
list2 = iter(list2)
value1 = next(list1)
value2 = next(list2)
# We'll normally exit this loop from a next() call raising StopIteration, which is
# how a generator function exits anyway.
while True:
if value1 <= value2:
# Yield the lower value.
yield value1
try:
# Grab the next value from list1.
value1 = next(list1)
except StopIteration:
# list1 is empty. Yield the last value we received from list2, then
# yield the rest of list2.
yield value2
while True:
yield next(list2)
else:
yield value2
try:
value2 = next(list2)
except StopIteration:
# list2 is empty.
yield value1
while True:
yield next(list1)