Cách tiếp cận ngây thơ
def transpose_finite_iterable(iterable):
return zip(*iterable) # `itertools.izip` for Python 2 users
hoạt động tốt đối với các lần lặp hữu hạn (ví dụ như các chuỗi như list
/ tuple
/ str
) của các lần lặp (có khả năng vô hạn) có thể được minh họa như
| |a_00| |a_10| ... |a_n0| |
| |a_01| |a_11| ... |a_n1| |
| |... | |... | ... |... | |
| |a_0i| |a_1i| ... |a_ni| |
| |... | |... | ... |... | |
Ở đâu
n in ℕ
,
a_ij
tương ứng với j
phần tử i
-th của -th iterable,
và sau khi áp dụng transpose_finite_iterable
chúng tôi nhận được
| |a_00| |a_01| ... |a_0i| ... |
| |a_10| |a_11| ... |a_1i| ... |
| |... | |... | ... |... | ... |
| |a_n0| |a_n1| ... |a_ni| ... |
Ví dụ Python về trường hợp như vậy trong đó 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)
Nhưng chúng ta không thể sử dụng transpose_finite_iterable
lại để trở về cấu trúc của bản gốc iterable
vì result
là một số lần lặp vô hạn của các số lần lặp hữu hạn ( tuple
trong trường hợp của chúng ta):
>>> transpose_finite_iterable(result)
... hangs ...
Traceback (most recent call last):
File "...", line 1, in ...
File "...", line 2, in transpose_finite_iterable
MemoryError
Vậy làm thế nào chúng ta có thể đối phó với trường hợp này?
... và đây là deque
Sau khi chúng ta xem tài liệu về itertools.tee
hàm , có một công thức Python mà với một số sửa đổi có thể giúp ích trong trường hợp của chúng ta
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))
hãy kiểm tra
>>> 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
Tổng hợp
Bây giờ chúng ta có thể định nghĩa hàm chung để làm việc với các iterables của các iterables là hữu hạn và các hàm khác là vô hạn khi sử dụng functools.singledispatch
trang trí như
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)
có thể được coi là nghịch đảo của chính nó (các nhà toán học gọi loại hàm này là "sự tham gia" ) trong lớp các toán tử nhị phân trên các phép lặp không hữu hạn.
Như một phần thưởng của singledispatch
ing, chúng ta có thể xử lý numpy
các mảng như
import numpy as np
...
transpose.register(np.ndarray, np.transpose)
và sau đó sử dụng nó như
>>> array = np.arange(4).reshape((2,2))
>>> array
array([[0, 1],
[2, 3]])
>>> transpose(array)
array([[0, 2],
[1, 3]])
Ghi chú
Vì transpose
trả về các trình vòng lặp và nếu ai đó muốn có một tuple
điểm list
giống như trong OP - điều này có thể được thực hiện bổ sung với map
chức năng tích hợp sẵn như
>>> original = [('a', 1), ('b', 2), ('c', 3), ('d', 4)]
>>> tuple(map(list, transpose(original)))
(['a', 'b', 'c', 'd'], [1, 2, 3, 4])
Quảng cáo
Tôi đã thêm giải pháp tổng quát vào lz
gói từ 0.5.0
phiên bản có thể được sử dụng như
>>> 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
Không có giải pháp (ít nhất là rõ ràng) để xử lý khả năng lặp vô hạn của các lần lặp có khả năng vô hạn, nhưng trường hợp này ít phổ biến hơn.