[pypy-svn] pypy default: Implemented itertools.combinations.

alex_gaynor commits-noreply at bitbucket.org
Thu Jan 20 19:48:40 CET 2011


Author: Alex Gaynor <alex.gaynor at gmail.com>
Branch: 
Changeset: r41078:834bbf3a3167
Date: 2011-01-20 12:48 -0600
http://bitbucket.org/pypy/pypy/changeset/834bbf3a3167/

Log:	Implemented itertools.combinations.

diff --git a/pypy/module/itertools/__init__.py b/pypy/module/itertools/__init__.py
--- a/pypy/module/itertools/__init__.py
+++ b/pypy/module/itertools/__init__.py
@@ -26,6 +26,7 @@
 
     interpleveldefs = {
         'chain'         : 'interp_itertools.W_Chain',
+        'combinations'  : 'interp_itertools.W_Combinations',
         'compress'      : 'interp_itertools.W_Compress',
         'count'         : 'interp_itertools.W_Count',
         'cycle'         : 'interp_itertools.W_Cycle',

diff --git a/pypy/module/itertools/test/test_itertools.py b/pypy/module/itertools/test/test_itertools.py
--- a/pypy/module/itertools/test/test_itertools.py
+++ b/pypy/module/itertools/test/test_itertools.py
@@ -758,3 +758,13 @@
         prod = product('abc', repeat=0)
         assert prod.next() == ()
         raises (StopIteration, prod.next)
+
+    def test_combinations(self):
+        from itertools import combinations
+
+        raises(TypeError, combinations, "abc")
+        raises(TypeError, combinations, "abc", 2, 1)
+        raises(TypeError, None)
+        raises(ValueError, combinations, "abc", -2)
+        assert list(combinations("abc", 32)) == []
+        assert list(combinations(range(4), 3)) == [(0, 1, 2), (0, 1, 3), (0, 2, 3), (1, 2, 3)]

diff --git a/pypy/module/itertools/interp_itertools.py b/pypy/module/itertools/interp_itertools.py
--- a/pypy/module/itertools/interp_itertools.py
+++ b/pypy/module/itertools/interp_itertools.py
@@ -1100,3 +1100,74 @@
            for prod in result:
                yield tuple(prod)
 """)
+
+class W_Combinations(Wrappable):
+    def __init__(self, space, pool_w, indices, r):
+        self.pool_w = pool_w
+        self.indices = indices
+        self.r = r
+        self.last_result_w = None
+        self.stopped = r > len(pool_w)
+
+    @unwrap_spec(ObjSpace, W_Root, W_Root, int)
+    def descr__new__(space, w_subtype, w_iterable, r):
+        pool_w = space.fixedview(w_iterable)
+        n = len(pool_w)
+        if r < 0:
+            raise OperationError(space.w_ValueError,
+                space.wrap("r must be non-negative")
+            )
+        indices = list(xrange(r))
+        return W_Combinations(space, pool_w, indices, r)
+
+    @unwrap_spec("self", ObjSpace)
+    def descr__iter__(self, space):
+        return self
+
+    @unwrap_spec("self", ObjSpace)
+    def descr_next(self, space):
+        if self.stopped:
+            raise OperationError(space.w_StopIteration, space.w_None)
+        if self.last_result_w is None:
+            # On the first pass, initialize result tuple using the indices
+            result_w = [None] * self.r
+            for i in xrange(self.r):
+                index = self.indices[i]
+                result_w[i] = self.pool_w[index]
+        else:
+            # Copy the previous result
+            result_w = self.last_result_w[:]
+            # Scan indices right-to-left until finding one that is not at its
+            # maximum (i + n - r).
+            i = self.r - 1
+            while i >= 0 and self.indices[i] == i + len(self.pool_w) - self.r:
+                i -= 1
+
+            # If i is negative, then the indices are all at their maximum value
+            # and we're done
+            if i < 0:
+                self.stopped = True
+                raise OperationError(space.w_StopIteration, space.w_None)
+
+            # Increment the current index which we know is not at its maximum.
+            # Then move back to the right setting each index to its lowest
+            # possible value (one higher than the index to its left -- this
+            # maintains the sort order invariant).
+            self.indices[i] += 1
+            for j in xrange(i + 1, self.r):
+                self.indices[j] = self.indices[j-1] + 1
+
+            # Update the result for the new indices starting with i, the
+            # leftmost index that changed
+            for i in xrange(i, self.r):
+                index = self.indices[i]
+                w_elem = self.pool_w[index]
+                result_w[i] = w_elem
+        self.last_result_w = result_w
+        return space.newtuple(result_w)
+
+W_Combinations.typedef = TypeDef("combinations",
+    __new__ = interp2app(W_Combinations.descr__new__.im_func),
+    __iter__ = interp2app(W_Combinations.descr__iter__),
+    next = interp2app(W_Combinations.descr_next),
+)


More information about the Pypy-commit mailing list