From 3b3cf3302db84f9ba9451a9d3edee13144515217 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Mon, 22 Aug 2022 15:07:16 -0500 Subject: [PATCH 1/4] Cleaned up interleave. --- cytoolz/itertoolz.pxd | 3 +-- cytoolz/itertoolz.pyx | 49 ++++++++++++++++--------------------------- 2 files changed, 19 insertions(+), 33 deletions(-) diff --git a/cytoolz/itertoolz.pxd b/cytoolz/itertoolz.pxd index 57e0609..39c8b85 100644 --- a/cytoolz/itertoolz.pxd +++ b/cytoolz/itertoolz.pxd @@ -36,9 +36,8 @@ cdef object c_merge_sorted(object seqs, object key=*) cdef class interleave: cdef list iters - cdef list newiters cdef Py_ssize_t i - cdef Py_ssize_t n + cdef int active cdef class _unique_key: diff --git a/cytoolz/itertoolz.pyx b/cytoolz/itertoolz.pyx index 0cce8d5..d86ab42 100644 --- a/cytoolz/itertoolz.pyx +++ b/cytoolz/itertoolz.pyx @@ -328,54 +328,41 @@ cdef class interleave: Returns a lazy iterator """ def __cinit__(self, seqs): - self.iters = [iter(seq) for seq in seqs] - self.newiters = [] + self.iters = list(map(iter, seqs)) self.i = 0 - self.n = PyList_GET_SIZE(self.iters) + self.active = PyList_GET_SIZE(self.iters) def __iter__(self): return self def __next__(self): - # This implementation is similar to what is done in `toolz` in that we - # construct a new list of iterators, `self.newiters`, when a value is - # successfully retrieved from an iterator from `self.iters`. + cdef object itrobj, val + cdef list iters cdef PyObject *obj - cdef object val + cdef int _len - if self.i == self.n: - self.n = PyList_GET_SIZE(self.newiters) - self.i = 0 - if self.n == 0: - raise StopIteration - self.iters = self.newiters - self.newiters = [] - val = PyList_GET_ITEM(self.iters, self.i) - self.i += 1 - obj = PtrIter_Next(val) + iters = self.iters + _len = PyList_GET_SIZE(iters) + + itrobj = PyList_GET_ITEM(iters, self.i) + obj = PtrIter_Next(itrobj) - # TODO: optimization opportunity. Previously, it was possible to - # continue on given exceptions, `self.pass_exceptions`, which is - # why this code is structured this way. Time to clean up? while obj is NULL: + # Check if error occurred obj = PyErr_Occurred() if obj is not NULL: + # Iterator raised an exception val = obj PyErr_Clear() raise val - if self.i == self.n: - self.n = PyList_GET_SIZE(self.newiters) - self.i = 0 - if self.n == 0: - raise StopIteration - self.iters = self.newiters - self.newiters = [] - val = PyList_GET_ITEM(self.iters, self.i) - self.i += 1 - obj = PtrIter_Next(val) + self.active = max(self.active - 1, 0) + if self.active == 0: + raise StopIteration - PyList_Append(self.newiters, val) + self.i = (self.i + 1) % _len + itrobj = PyList_GET_ITEM(iters, self.i) + obj = PtrIter_Next(itrobj) val = obj Py_XDECREF(obj) return val From 30348b548b6d3127fd60bc0f2a4e0830bfc6a6f2 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Mon, 22 Aug 2022 15:19:03 -0500 Subject: [PATCH 2/4] Fix incrementation. --- cytoolz/itertoolz.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cytoolz/itertoolz.pyx b/cytoolz/itertoolz.pyx index d86ab42..f1d2c1a 100644 --- a/cytoolz/itertoolz.pyx +++ b/cytoolz/itertoolz.pyx @@ -345,6 +345,7 @@ cdef class interleave: _len = PyList_GET_SIZE(iters) itrobj = PyList_GET_ITEM(iters, self.i) + self.i = (self.i + 1) % _len obj = PtrIter_Next(itrobj) while obj is NULL: @@ -360,8 +361,8 @@ cdef class interleave: if self.active == 0: raise StopIteration - self.i = (self.i + 1) % _len itrobj = PyList_GET_ITEM(iters, self.i) + self.i = (self.i + 1) % _len obj = PtrIter_Next(itrobj) val = obj Py_XDECREF(obj) From e9b059c852f9492de2418573746a500a3850fced Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Mon, 22 Aug 2022 15:33:46 -0500 Subject: [PATCH 3/4] Avoid using mod in favor of simpler wraparound. --- cytoolz/itertoolz.pyx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cytoolz/itertoolz.pyx b/cytoolz/itertoolz.pyx index f1d2c1a..33e991f 100644 --- a/cytoolz/itertoolz.pyx +++ b/cytoolz/itertoolz.pyx @@ -345,7 +345,9 @@ cdef class interleave: _len = PyList_GET_SIZE(iters) itrobj = PyList_GET_ITEM(iters, self.i) - self.i = (self.i + 1) % _len + self.i += 1 + if self.i == _len: + self.i = 0 obj = PtrIter_Next(itrobj) while obj is NULL: @@ -362,7 +364,9 @@ cdef class interleave: raise StopIteration itrobj = PyList_GET_ITEM(iters, self.i) - self.i = (self.i + 1) % _len + self.i += 1 + if self.i == _len: + self.i = 0 obj = PtrIter_Next(itrobj) val = obj Py_XDECREF(obj) From c6ddf13dcd2f21023929edcad551666a8c35fd74 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Mon, 22 Aug 2022 18:08:44 -0500 Subject: [PATCH 4/4] Use correct int type. --- cytoolz/itertoolz.pxd | 2 +- cytoolz/itertoolz.pyx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cytoolz/itertoolz.pxd b/cytoolz/itertoolz.pxd index 39c8b85..ba4872c 100644 --- a/cytoolz/itertoolz.pxd +++ b/cytoolz/itertoolz.pxd @@ -37,7 +37,7 @@ cdef object c_merge_sorted(object seqs, object key=*) cdef class interleave: cdef list iters cdef Py_ssize_t i - cdef int active + cdef Py_ssize_t active cdef class _unique_key: diff --git a/cytoolz/itertoolz.pyx b/cytoolz/itertoolz.pyx index 33e991f..0b626c7 100644 --- a/cytoolz/itertoolz.pyx +++ b/cytoolz/itertoolz.pyx @@ -339,7 +339,7 @@ cdef class interleave: cdef object itrobj, val cdef list iters cdef PyObject *obj - cdef int _len + cdef Py_ssize_t _len iters = self.iters _len = PyList_GET_SIZE(iters)