Krytyka innych odpowiedzi tutaj:
Żadna z tych odpowiedzi nie jest kawałkami o równej wielkości, wszystkie pozostawiają fragment runtu na końcu, więc nie są całkowicie zrównoważone. Jeśli użyjesz tych funkcji do rozłożenia pracy, masz wbudowaną perspektywę, że jedna z nich prawdopodobnie zakończy się znacznie wcześniej niż inne, więc siedziałaby bezczynnie, podczas gdy inne nadal ciężko pracowały.
Na przykład bieżąca górna odpowiedź kończy się na:
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[70, 71, 72, 73, 74]]
Po prostu nienawidzę tego wyścigu!
Inni, jak list(grouper(3, xrange(7)))
, i chunk(xrange(7), 3)
zarówno zwrot: [(0, 1, 2), (3, 4, 5), (6, None, None)]
. Są None
to tylko wypełnienia i moim zdaniem raczej nieeleganckie. NIE są równomiernie dzielące iteracyjne.
Dlaczego nie możemy ich lepiej podzielić?
Moje rozwiązanie
Oto wyważone rozwiązanie, dostosowane z funkcji Użyłem w produkcji (Uwaga: W Pythonie 3 do zastąpienia xrange
z range
):
def baskets_from(items, maxbaskets=25):
baskets = [[] for _ in xrange(maxbaskets)] # in Python 3 use range
for i, item in enumerate(items):
baskets[i % maxbaskets].append(item)
return filter(None, baskets)
I stworzyłem generator, który robi to samo, jeśli umieścisz go na liście:
def iter_baskets_from(items, maxbaskets=3):
'''generates evenly balanced baskets from indexable iterable'''
item_count = len(items)
baskets = min(item_count, maxbaskets)
for x_i in xrange(baskets):
yield [items[y_i] for y_i in xrange(x_i, item_count, baskets)]
I wreszcie, ponieważ widzę, że wszystkie powyższe funkcje zwracają elementy w ciągłej kolejności (tak jak je podano):
def iter_baskets_contiguous(items, maxbaskets=3, item_count=None):
'''
generates balanced baskets from iterable, contiguous contents
provide item_count if providing a iterator that doesn't support len()
'''
item_count = item_count or len(items)
baskets = min(item_count, maxbaskets)
items = iter(items)
floor = item_count // baskets
ceiling = floor + 1
stepdown = item_count % baskets
for x_i in xrange(baskets):
length = ceiling if x_i < stepdown else floor
yield [items.next() for _ in xrange(length)]
Wynik
Aby je przetestować:
print(baskets_from(xrange(6), 8))
print(list(iter_baskets_from(xrange(6), 8)))
print(list(iter_baskets_contiguous(xrange(6), 8)))
print(baskets_from(xrange(22), 8))
print(list(iter_baskets_from(xrange(22), 8)))
print(list(iter_baskets_contiguous(xrange(22), 8)))
print(baskets_from('ABCDEFG', 3))
print(list(iter_baskets_from('ABCDEFG', 3)))
print(list(iter_baskets_contiguous('ABCDEFG', 3)))
print(baskets_from(xrange(26), 5))
print(list(iter_baskets_from(xrange(26), 5)))
print(list(iter_baskets_contiguous(xrange(26), 5)))
Który drukuje:
[[0], [1], [2], [3], [4], [5]]
[[0], [1], [2], [3], [4], [5]]
[[0], [1], [2], [3], [4], [5]]
[[0, 8, 16], [1, 9, 17], [2, 10, 18], [3, 11, 19], [4, 12, 20], [5, 13, 21], [6, 14], [7, 15]]
[[0, 8, 16], [1, 9, 17], [2, 10, 18], [3, 11, 19], [4, 12, 20], [5, 13, 21], [6, 14], [7, 15]]
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13, 14], [15, 16, 17], [18, 19], [20, 21]]
[['A', 'D', 'G'], ['B', 'E'], ['C', 'F']]
[['A', 'D', 'G'], ['B', 'E'], ['C', 'F']]
[['A', 'B', 'C'], ['D', 'E'], ['F', 'G']]
[[0, 5, 10, 15, 20, 25], [1, 6, 11, 16, 21], [2, 7, 12, 17, 22], [3, 8, 13, 18, 23], [4, 9, 14, 19, 24]]
[[0, 5, 10, 15, 20, 25], [1, 6, 11, 16, 21], [2, 7, 12, 17, 22], [3, 8, 13, 18, 23], [4, 9, 14, 19, 24]]
[[0, 1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20], [21, 22, 23, 24, 25]]
Zauważ, że ciągły generator zapewnia fragmenty o takich samych wzorcach długości jak pozostałe dwa, ale wszystkie elementy są w porządku i są one równomiernie podzielone, tak jak można podzielić listę odrębnych elementów.