<div dir="ltr">After correcting Terry's off-by-one error, I get this on Python 3.6 (and bumping NUMBER to 1000).  In speed, Terry's is either very slightly faster or very slightly slower than the recipe, depending on the data.  I think Terry's is more readable that roundrobin(), but still requires a little thought compared to Bunslow's.  However, using the builtin name 'next' as a variable name seems like a bad choice to me (it doesn't break in this code, but seems like bad practice, i'd choose some other variable name).<div><br></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><div><div><font face="monospace, monospace">507-code % python rr_tester.py</font></div></div><div><div><font face="monospace, monospace">testing with equal_data</font></div></div><div><div><font face="monospace, monospace">  roundrobin      0.187816</font></div></div><div><div><font face="monospace, monospace">  bunslow         0.393162</font></div></div><div><div><font face="monospace, monospace">  terry           0.183851</font></div></div><div><div><font face="monospace, monospace">  stackoverflow   0.675543</font></div></div><div><div><font face="monospace, monospace">**************************************************</font></div></div><div><div><font face="monospace, monospace">testing with unequal_data</font></div></div><div><div><font face="monospace, monospace">  roundrobin      0.209959</font></div></div><div><div><font face="monospace, monospace">  bunslow         0.555731</font></div></div><div><div><font face="monospace, monospace">  terry           0.233015</font></div></div><div><div><font face="monospace, monospace">  stackoverflow   0.746880</font></div></div><div><div><font face="monospace, monospace">**************************************************</font></div></div><div><div><font face="monospace, monospace">testing with extreme_data</font></div></div><div><div><font face="monospace, monospace">  roundrobin      0.053149</font></div></div><div><div><font face="monospace, monospace">  bunslow         0.273607</font></div></div><div><div><font face="monospace, monospace">  terry           0.051963</font></div></div><div><div><font face="monospace, monospace">  stackoverflow   0.158515</font></div></div><div><font face="monospace, monospace"><br></font></div><div><div><font face="monospace, monospace">508-code % python --version</font></div></div><div><div><font face="monospace, monospace">Python 3.6.2 :: Anaconda custom (x86_64)</font></div></div></blockquote></div><div class="gmail_extra"><br><div class="gmail_quote">On Mon, Nov 20, 2017 at 9:15 AM, Terry Reedy <span dir="ltr"><<a href="mailto:tjreedy@udel.edu" target="_blank">tjreedy@udel.edu</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">On 11/20/2017 11:08 AM, Steven D'Aprano wrote:<br>
<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Please don't make claims about correctness and efficiency without<br>
testing the code first. The second suggestion given there, using deque,<br>
is *not* correct as provided, as it fails to work with iterables. It<br>
requires the caller to pass only iterators, unlike the existing<br>
roundrobin recipe which accepts any iterable.<br>
<br>
Nor is it more efficient, at least on my machine -- in fact the<br>
opposite, it is the worst performing of the four recipes I've tried:<br>
<br>
- the current recipe from the itertools docs;<br>
- your re-write, using zip_longest;<br>
- Terry's version;<br>
- and the one from stackoverflow.<br>
<br>
I've attached my test code, in case you want to play around with it.<br>
Apologies in advance for any bugs in the test code (its 2 in the<br>
morning here and I've had a long day).<br>
<br>
According to my testing, on my computer using Python 3.5, Terry's code<br>
is by far the fastest in all three separate test cases, but that<br>
probably shouldn't count since it's buggy (it truncates the results and<br>
bails out early under some circumstances). Out of the implementations<br>
that don't truncate, the existing recipe is by far the fastest.<br>
<br>
Terry, if you're reading this, try:<br>
<br>
list(roundrobin('A', 'B', 'CDE'))<br>
</blockquote>
<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Your version truncates the results to A B C instead of A B C D E as the<br>
itertools recipe gives.<br>
</blockquote>
<br></span>
This is due to an off-by-1 error which I corrected 3 hours later in a follow-up post, repeated below.<br>
---<br>
<br>
Correct off-by-one error.  I should have tested with an edge case such as<br>
print(list(roundrobin('ABC', '')))<br>
<br>
> The following combines 3 statements into one for statement.<br>
><br>
> def roundrobin(*iterables):<span class=""><br>
>      "roundrobin('ABC', 'D', 'EF') --> A D E B F C"<br></span>
>      nexts = cycle(iter(it).__next__ for it in iterables)<br>
>      for reduced_len in reversed(range(1, len(iterables))):<br>
<br>
Make that 0 rather than 1 for start value.<br>
<br>
>          try:<br>
>              for next in nexts:<br>
>                  yield next()<br>
>          except StopIteration:<br>
>              nexts = cycle(islice(nexts, reduced_len))<br>
<br>
A slightly clearer, slightly less efficient alternative would be<br>
<br>
def roundrobin(*iterables):<span class=""><br>
    "roundrobin('ABC', 'D', 'EF') --> A D E B F C"<br></span>
    nexts = cycle(iter(it).__next__ for it in iterables)<br>
    for current_len in reversed(range(1, len(iterables)+1)):<br>
        try:<br>
            for next in nexts:<br>
                yield next()<br>
        except StopIteration:<br>
            nexts = cycle(islice(nexts, current_len - 1))<span class="HOEnZb"><font color="#888888"><br>
<br>
-- <br>
Terry Jan Reedy</font></span><div class="HOEnZb"><div class="h5"><br>
<br>
______________________________<wbr>_________________<br>
Python-ideas mailing list<br>
<a href="mailto:Python-ideas@python.org" target="_blank">Python-ideas@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/python-ideas" rel="noreferrer" target="_blank">https://mail.python.org/mailma<wbr>n/listinfo/python-ideas</a><br>
Code of Conduct: <a href="http://python.org/psf/codeofconduct/" rel="noreferrer" target="_blank">http://python.org/psf/codeofco<wbr>nduct/</a><br>
</div></div></blockquote></div><br><br clear="all"><div><br></div>-- <br><div class="gmail_signature" data-smartmail="gmail_signature">Keeping medicines from the bloodstreams of the sick; food <br>from the bellies of the hungry; books from the hands of the <br>uneducated; technology from the underdeveloped; and putting <br>advocates of freedom in prisons.  Intellectual property is<br>to the 21st century what the slave trade was to the 16th.<br></div>
</div>