patternpythonMinor
Interleaving values from several iterables
Viewed 0 times
interleavingiterablesseveralvaluesfrom
Problem
I am new to Python and am trying to see if there is a cleaner way to write my code. The following code take iterables as parameter it produces the first value from the first parameter, then the first value from the the second parameter, ..., then the first value from the last parameter; then the second value from the first parameter and so on.If any iteratable produces no more values, this generator produces no more values.
If I do:
produces the values 'a', 'f', 'h', 'b', 'g', 'i', and 'c'. The code I've written is as under(which works):
Is there a cleaner/ concise way to write the code(without using any external libraries)?
If I do:
for i in skip('abcde','fg','hijk'):
print(i,end='')produces the values 'a', 'f', 'h', 'b', 'g', 'i', and 'c'. The code I've written is as under(which works):
def skip(*args):
itrs = [iter(arg) for arg in args]
while itrs != []:
temp = []
for i in range(len(itrs)):
try:
yield next(itrs[i])
temp.append(itrs[i])
except StopIteration:
pass
itrs = tempIs there a cleaner/ concise way to write the code(without using any external libraries)?
Solution
First off, I think there is an error in the original code. The code produces
Let's look at the for loop in the middle
No where in the body operate on the index, so every
Now lets look at the
This basically gives the code a way to exit when everything is finished. However, if you think about how the code works, the only time
So the while loop can become a while True loop and
Now we need to convince ourselves that the code will actually return at some point. This isn't too hard, as long as one of the iterable passed in is finite i.e. has an end, we will reach
But there is another problem. What if
This gives us the final code:
With this
outputs
is a noop
As an aside, the indentation in the original code is inconsistent, which makes it un-runnable. Please be sure to post usable code next time
afhbgicjdke instead of the expected afhbgic. I think the intention was to return instead of pass in the stop iteration block, the answer below work under this assumption.Let's look at the for loop in the middle
for i in range(len(itrs)):
try:
yield next(itrs[i])
temp.append(itrs[i])
except StopIteration:
returnNo where in the body operate on the index, so every
itrs[i] can be replaced with just i with a simpler for i in iters. This gives usfor i in itrs:
try:
yield next(i)
temp.append(i)
except StopIteration:
returnNow lets look at the
while bodywhile itrs != []:
temp = []
# ... the for loop that includes an append to itrs
itrs = tempThis basically gives the code a way to exit when everything is finished. However, if you think about how the code works, the only time
itrs has a lower length than args is when one of the itrs reaches StopIteration. In that case, we would have returned already!So the while loop can become a while True loop and
temp and it's usage can be ommited.def skip(*args):
itrs = [iter(arg) for arg in args]
while True:
for i in itrs:
try:
yield next(i)
except StopIteration:
returnNow we need to convince ourselves that the code will actually return at some point. This isn't too hard, as long as one of the iterable passed in is finite i.e. has an end, we will reach
StopIteration at some point. And in the case that every iterable is infinite, the original code would run forever too. So we are good there.But there is another problem. What if
skip is called with no argument? This will result in us never returning from the function, since there is no iterable for us to trigger StopIteration! In the original code, this is dealt with implicitly through temp being empty. Since we don't have that anymore, we have to handle it explicitly.This gives us the final code:
def skip(*args):
if len(args) == 0:
return
itrs = [iter(arg) for arg in args]
while True:
for i in itrs:
try:
yield next(i)
except StopIteration:
returnWith this
for i in skip('abcde','fg','hijk'):
print(i,end='')outputs
afhbgic andfor i in skip():
print(i,end='')is a noop
As an aside, the indentation in the original code is inconsistent, which makes it un-runnable. Please be sure to post usable code next time
Code Snippets
for i in range(len(itrs)):
try:
yield next(itrs[i])
temp.append(itrs[i])
except StopIteration:
returnfor i in itrs:
try:
yield next(i)
temp.append(i)
except StopIteration:
returnwhile itrs != []:
temp = []
# ... the for loop that includes an append to itrs
itrs = tempdef skip(*args):
itrs = [iter(arg) for arg in args]
while True:
for i in itrs:
try:
yield next(i)
except StopIteration:
returndef skip(*args):
if len(args) == 0:
return
itrs = [iter(arg) for arg in args]
while True:
for i in itrs:
try:
yield next(i)
except StopIteration:
returnContext
StackExchange Code Review Q#78549, answer score: 7
Revisions (0)
No revisions yet.