Python 结构化一维序列,有时需要通过某种形式将一维序列转换为复合序列,与前面的处理过程相比,可能更麻烦一些。可以用itertools
模块的groupby()
函数进行处理。
假设现有如下一个一维列表:
flat = ['2', '3', '5', '7', '11', '13', '17', '19', '23', '29',
'31', '37', '41', '43', '47', '53', '59', '61', '67', '71',
... ]
使用嵌套生成器函数,可以把它从一维序列转换为复合序列。为了实现这一点,需要一个可多次使用的简单迭代器,表达式如下所示:
>>> flat_iter = iter(flat)
>>> (tuple(next(flat_iter) for i in range(5))
... for row in range(len(flat)//5)
... )
<generator object <genexpr> at 0x101cead70>
>>> list(_)
[('2', '3', '5', '7', '11'),
('13', '17', '19', '23', '29'),
('31', '37', '41', '43', '47'),
('53', '59', '61', '67', '71'),
...
]
首先,在双层循环外部创建了一个用于生成复合序列的迭代器,生成器表达式使用tuple(next(flat_iter) for i in range(5))
,基于变量flat_iter
中的可迭代对象创建五元组。该表达式嵌于外层更大的生成器内,此生成器负责按照指定次数循环执行内层循环,生成最终的复合序列。
该表达式只适用于作为输入的一维列表能被子序列平分的情况,如果最后一行有剩余的元素,需要单独处理。
可以先使用上面的函数获得相同长度的元组,然后用下面的函数处理剩余长度不同的部分:
ItemType = TypeVar("ItemType")
Flat = Sequence[ItemType]
Grouped = List[Tuple[ItemType, ...]]
def group_by_seq(n: int, sequence: Flat) -> Grouped:
flat_iter=iter(sequence)
full_sized_items = list(tuple(next(flat_iter)
for i in range(n))
for row in range(len(sequence)//n))
trailer = tuple(flat_iter)
if trailer:
return full_sized_items + [trailer]
else:
return full_sized_items
首先将长度为n
的元组组成的列表提取到full_sized_items
中,如果元素还有剩余,则将剩余元素组成一个元组,追加到full_sized_items
后面。如果元组长度为0,则忽略trailer
元组,返回原来的列表。
类型标示中包含了一个类型变量的抽象定义ItemType
,用于表示函数的输入类型和输出类型是一致的,字符串序列或者浮点序列皆可。
函数的输入是由数据项组成的序列,输出是一个列表,元素为相同类型的数据项组成的元组。这里的数据项用ItemType
表示,可以是任何类型。
这个实现不如之前的算法简洁,函数式特征也太不明显。可以把它改写成一个简单的生成器函数,生成可迭代对象而不是列表。
下面的代码使用while
循环实现算法逻辑,并结合了尾递归优化:
ItemType = TypeVar("ItemType")
Flat_Iter = Iterator[ItemType]
Grouped_Iter = Iterator[Tuple[ItemType, ...]]
def group_by_iter(n: int, iterable: Flat_Iter) -> Grouped_Iter:
row = tuple(next(iterable) for i in range(n))
while row:
yield row
row = tuple(next(iterable) for i in range(n))
该函数从输入可迭代对象中抽取指定长度的行,当迭代至输入数据尾部时,tuple(next (iterable) for i in range(n))
将会返回一个空元组,这是递归的基础型式,也是while
循环的结束判断条件。
类型标示相应地修改成了包含迭代器的类型,不限于处理序列类型。由于显式使用了next()
函数,需要这样使用:group_by_iter(7, iter(flat))
。必须使用iter()
函数将集合转换为迭代器。