Pendekatan naif
def transpose_finite_iterable(iterable):
return zip(*iterable) # `itertools.izip` for Python 2 users
berfungsi dengan baik untuk iterable terbatas (misalnya urutan seperti list
/ tuple
/ str
) dari iterables (berpotensi tak terbatas) yang dapat diilustrasikan seperti
| |a_00| |a_10| ... |a_n0| |
| |a_01| |a_11| ... |a_n1| |
| |... | |... | ... |... | |
| |a_0i| |a_1i| ... |a_ni| |
| |... | |... | ... |... | |
dimana
n in ℕ
,
a_ij
sesuai dengan j
elemen i
-th dari iterable,
dan setelah mendaftar transpose_finite_iterable
kita dapatkan
| |a_00| |a_01| ... |a_0i| ... |
| |a_10| |a_11| ... |a_1i| ... |
| |... | |... | ... |... | ... |
| |a_n0| |a_n1| ... |a_ni| ... |
Contoh python dari kasus seperti itu di mana a_ij == j
,n == 2
>>> from itertools import count
>>> iterable = [count(), count()]
>>> result = transpose_finite_iterable(iterable)
>>> next(result)
(0, 0)
>>> next(result)
(1, 1)
Tetapi kita tidak dapat menggunakan transpose_finite_iterable
lagi untuk kembali ke struktur asli iterable
karena result
iterable iterable terbatas hingga terbatas ( tuple
dalam kasus kami):
>>> transpose_finite_iterable(result)
... hangs ...
Traceback (most recent call last):
File "...", line 1, in ...
File "...", line 2, in transpose_finite_iterable
MemoryError
Jadi bagaimana kita bisa menangani kasus ini?
... dan ini dia deque
Setelah kita melihat pada docs of itertools.tee
function , ada resep Python yang dengan beberapa modifikasi dapat membantu dalam kasus kita
def transpose_finite_iterables(iterable):
iterator = iter(iterable)
try:
first_elements = next(iterator)
except StopIteration:
return ()
queues = [deque([element])
for element in first_elements]
def coordinate(queue):
while True:
if not queue:
try:
elements = next(iterator)
except StopIteration:
return
for sub_queue, element in zip(queues, elements):
sub_queue.append(element)
yield queue.popleft()
return tuple(map(coordinate, queues))
mari kita periksa
>>> from itertools import count
>>> iterable = [count(), count()]
>>> result = transpose_finite_iterables(transpose_finite_iterable(iterable))
>>> result
(<generator object transpose_finite_iterables.<locals>.coordinate at ...>, <generator object transpose_finite_iterables.<locals>.coordinate at ...>)
>>> next(result[0])
0
>>> next(result[0])
1
Perpaduan
Sekarang kita dapat mendefinisikan fungsi umum untuk bekerja dengan iterables dari iterables yang terbatas dan yang lainnya berpotensi tak terbatas menggunakan functools.singledispatch
dekorator seperti
from collections import (abc,
deque)
from functools import singledispatch
@singledispatch
def transpose(object_):
"""
Transposes given object.
"""
raise TypeError('Unsupported object type: {type}.'
.format(type=type))
@transpose.register(abc.Iterable)
def transpose_finite_iterables(object_):
"""
Transposes given iterable of finite iterables.
"""
iterator = iter(object_)
try:
first_elements = next(iterator)
except StopIteration:
return ()
queues = [deque([element])
for element in first_elements]
def coordinate(queue):
while True:
if not queue:
try:
elements = next(iterator)
except StopIteration:
return
for sub_queue, element in zip(queues, elements):
sub_queue.append(element)
yield queue.popleft()
return tuple(map(coordinate, queues))
def transpose_finite_iterable(object_):
"""
Transposes given finite iterable of iterables.
"""
yield from zip(*object_)
try:
transpose.register(abc.Collection, transpose_finite_iterable)
except AttributeError:
# Python3.5-
transpose.register(abc.Mapping, transpose_finite_iterable)
transpose.register(abc.Sequence, transpose_finite_iterable)
transpose.register(abc.Set, transpose_finite_iterable)
yang dapat dianggap sebagai kebalikannya sendiri (ahli matematika menyebut fungsi semacam ini "involusi" ) di kelas operator biner melalui iterables terbatas yang tidak kosong.
Sebagai bonus singledispatch
kami dapat menangani numpy
array seperti
import numpy as np
...
transpose.register(np.ndarray, np.transpose)
dan kemudian gunakan seperti
>>> array = np.arange(4).reshape((2,2))
>>> array
array([[0, 1],
[2, 3]])
>>> transpose(array)
array([[0, 2],
[1, 3]])
Catatan
Sejak transpose
kembali iterator dan jika seseorang ingin memiliki tuple
dari list
s seperti di OP - ini dapat dibuat juga dengan map
built-in fungsi seperti
>>> original = [('a', 1), ('b', 2), ('c', 3), ('d', 4)]
>>> tuple(map(list, transpose(original)))
(['a', 'b', 'c', 'd'], [1, 2, 3, 4])
Iklan
Saya telah menambahkan solusi umum ke lz
paket dari 0.5.0
versi yang dapat digunakan seperti
>>> from lz.transposition import transpose
>>> list(map(tuple, transpose(zip(range(10), range(10, 20)))))
[(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), (10, 11, 12, 13, 14, 15, 16, 17, 18, 19)]
PS
Tidak ada solusi (setidaknya jelas) untuk menangani iterable yang berpotensi tak terbatas dari iterables yang berpotensi tak terbatas, tetapi kasus ini lebih jarang terjadi.