วิธีการไร้เดียงสา
def transpose_finite_iterable(iterable):
return zip(*iterable) # `itertools.izip` for Python 2 users
ใช้งานได้ดีสำหรับ จำกัด iterable (เช่นลำดับเช่นlist
/ tuple
/ str
) ของ iterables (อาจไม่มีที่สิ้นสุด) ซึ่งสามารถแสดงได้เช่น
| |a_00| |a_10| ... |a_n0| |
| |a_01| |a_11| ... |a_n1| |
| |... | |... | ... |... | |
| |a_0i| |a_1i| ... |a_ni| |
| |... | |... | ... |... | |
ที่ไหน
n in ℕ
,
a_ij
สอดคล้องกับj
องค์ประกอบi
-th ของ-th iterable
และหลังจากการสมัครtranspose_finite_iterable
เราได้รับ
| |a_00| |a_01| ... |a_0i| ... |
| |a_10| |a_11| ... |a_1i| ... |
| |... | |... | ... |... | ... |
| |a_n0| |a_n1| ... |a_ni| ... |
ตัวอย่างเช่นงูหลามของคดีดังกล่าวที่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)
แต่เราไม่สามารถใช้transpose_finite_iterable
อีกครั้งเพื่อกลับไปที่โครงสร้างของต้นฉบับได้iterable
เนื่องจากresult
มีการวนซ้ำ iterables ที่ไม่สิ้นสุด ( tuple
s ในกรณีของเรา):
>>> transpose_finite_iterable(result)
... hangs ...
Traceback (most recent call last):
File "...", line 1, in ...
File "...", line 2, in transpose_finite_iterable
MemoryError
แล้วเราจะจัดการกับกรณีนี้ได้อย่างไร?
... และที่นี่มา deque
หลังจากที่เราดูเอกสารการitertools.tee
ทำงานมีสูตร Python ที่มีการแก้ไขบางอย่างสามารถช่วยในกรณีของเรา
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))
ตรวจสอบกันเถอะ
>>> 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
สังเคราะห์
ตอนนี้เราสามารถกำหนดฟังก์ชั่นทั่วไปสำหรับการทำงานกับ iterables ของ iterables ที่ จำกัด และอีกอันหนึ่งอาจจะไม่มีที่สิ้นสุดโดยใช้functools.singledispatch
มัณฑนากรเช่น
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)
ซึ่งถือได้ว่าเป็นตัวผกผันของมันเอง (นักคณิตศาสตร์เรียกฟังก์ชันนี้ว่า "involutions" ) ในชั้นเรียนของตัวดำเนินการแบบไบนารีบนการ จำกัด iterables ที่ไม่ว่างเปล่า
เป็นโบนัสในการรับเข้าsingledispatch
เราสามารถจัดการกับnumpy
อาร์เรย์ได้
import numpy as np
...
transpose.register(np.ndarray, np.transpose)
แล้วใช้มันเหมือน
>>> array = np.arange(4).reshape((2,2))
>>> array
array([[0, 1],
[2, 3]])
>>> transpose(array)
array([[0, 2],
[1, 3]])
บันทึก
เนื่องจากtranspose
ส่งคืนตัววนซ้ำและถ้ามีคนต้องการมีtuple
ของlist
ชอบใน OP - นี้สามารถทำเพิ่มเติมด้วยmap
ฟังก์ชั่นในตัวเช่น
>>> original = [('a', 1), ('b', 2), ('c', 3), ('d', 4)]
>>> tuple(map(list, transpose(original)))
(['a', 'b', 'c', 'd'], [1, 2, 3, 4])
การโฆษณา
ฉันได้เพิ่มโซลูชันทั่วไปให้กับlz
แพ็คเกจจาก0.5.0
เวอร์ชันที่สามารถใช้งานได้
>>> 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
ไม่มีวิธีแก้ปัญหา (อย่างน้อยก็ชัดเจน) สำหรับการจัดการ iterable ที่ไม่มีขีด จำกัด ของ iterable ที่อาจเกิดขึ้นได้ แต่กรณีนี้พบได้น้อยกว่า