Python中的迭代器选择器
是否有一种标准的python方法可以从提供的迭代器列表中选择一个值,而不推进未选择的迭代器 对于两个迭代器,有类似的情况(不要太难判断:它很快就被组合在一起,只是为了说明这个想法): 请注意,如果使用类似的方式:Python中的迭代器选择器,python,iterator,Python,Iterator,是否有一种标准的python方法可以从提供的迭代器列表中选择一个值,而不推进未选择的迭代器 对于两个迭代器,有类似的情况(不要太难判断:它很快就被组合在一起,只是为了说明这个想法): 请注意,如果使用类似的方式: [e1 if f(e1, e2) else e2 for (e1, e2) in zip(i1, i2)] 然后,未选择的迭代器每次都会前进,这不是我想要的。您可以将其发送回生成器: def iselect(i1, i2, f): while True: tr
[e1 if f(e1, e2) else e2 for (e1, e2) in zip(i1, i2)]
然后,未选择的迭代器每次都会前进,这不是我想要的。您可以将其发送回生成器:
def iselect(i1, i2, f):
while True:
try:
e1, e2 = next(i1), next(i2)
if f(e1, e2):
yield e1
i2.send(e2)
else:
yield e2
i1.send(e1)
except StopIteration:
return
我将使用一个“排序函数”来代替“选择函数”,它告诉我们哪个元素应该先去
程序首先创建一个2元组列表:(迭代器,当前值)。由于一个迭代器可能为空,因此必须使用try..catch
(即,它不能是紧凑型的)
第二,只要至少有一个迭代器,我们就迭代。排序函数将必须输出的元素放在第一位。这个元素是“屈服的”。然后,调用迭代器以获取下一个元素。如果没有更多的元素,迭代器将从列表中删除
这给出了以下代码
def iselect( list_of_iterators, sort_function ):
work_list = []
for i in list_of_iterators:
try:
new_item = ( i, next(i) ) # iterator and its first element
work_list.append( new_item )
except StopIteration:
pass # this iterator is empty, skip it
#
while len(work_list) > 0:
# this selects which element should go first
work_list.sort( lambda e1,e2: sort_function(e1[1],e2[1]) )
yield work_list[0][1]
# update the first element of the list
try:
i, e = work_list[0]
e = next(i)
work_list[0] = ( i, e )
except StopIteration:
work_list = work_list[1:]
为了测试这个程序(包括一个不产生任何结果的迭代器),我使用
该包有一个可查看的迭代器包装器。如果我对你的问题理解正确的话,这看起来应该是一个非常干净的解决方案。您需要查看一组迭代器的当前值,只需通过调用其上的next()来修改所选迭代器
测试此代码:
# sample input iterators for testing
# assume python 3.x so range function returns iterable
iters = [range(i,5) for i in range(4)]
# the following could be encapsulated...
peekables = [peekable(it) for it in iters]
# sample selection function, returns index of minimum
# value among those being compared, or StopIteration if
# one of the lists contains None
def selector_func(vals_list):
if None in vals_list:
raise StopIteration
else:
return vals_list.index(min(vals_list))
for val in iselect(peekables, selector_func):
print(val)
输出:
0
1
1
2
2
2
3
3
3
3
4
您可以使用itertools.chain将最后一个
项
放回迭代器
:
import itertools as IT
iterator = IT.chain([item], iterator)
对于许多迭代器:
items = map(next, iterators)
idx = f(*items)
iterators = [IT.chain([item], iterator) if i != idx else iterator
for i, (item, iterator) in enumerate(zip(items, iterators))]
比如说,
import itertools as IT
def iselect(f, *iterators):
iterators = map(iter, iterators)
while True:
try:
items = map(next, iterators)
except StopIteration:
return
idx = f(*items)
iterators = [IT.chain([item], iterator) if i != idx else iterator
for i, (item, iterator) in enumerate(zip(items, iterators))]
yield items[idx]
def foo(*args):
return sorted(range(len(args)), key=args.__getitem__)[0]
i1 = range(4)
i2 = range(4)
i3 = range(4)
for item in iselect(foo, i1, i2, i3):
print(item)
屈服
0
0
0
1
1
1
2
2
2
3
顺便说一下,这将在两个迭代器中的一个被耗尽后退出,即使另一个迭代器还有更多的元素要去。这就是我想要的行为吗?@Reti43是的,这正是我需要的(符合
zip
或izip
)我的理解是send()。此外,send()
仍会触发下一个要生成的值。如果迭代器是以一种你不能用send影响它们的方式构造的呢<代码>gen=(i代表xrange(5)中的i);下一代();gen.send(10)
。
items = map(next, iterators)
idx = f(*items)
iterators = [IT.chain([item], iterator) if i != idx else iterator
for i, (item, iterator) in enumerate(zip(items, iterators))]
import itertools as IT
def iselect(f, *iterators):
iterators = map(iter, iterators)
while True:
try:
items = map(next, iterators)
except StopIteration:
return
idx = f(*items)
iterators = [IT.chain([item], iterator) if i != idx else iterator
for i, (item, iterator) in enumerate(zip(items, iterators))]
yield items[idx]
def foo(*args):
return sorted(range(len(args)), key=args.__getitem__)[0]
i1 = range(4)
i2 = range(4)
i3 = range(4)
for item in iselect(foo, i1, i2, i3):
print(item)
0
0
0
1
1
1
2
2
2
3