]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Generalize reduce() to work with iterators.
authorTim Peters <tim.peters@gmail.com>
Fri, 4 May 2001 04:39:21 +0000 (04:39 +0000)
committerTim Peters <tim.peters@gmail.com>
Fri, 4 May 2001 04:39:21 +0000 (04:39 +0000)
NEEDS DOC CHANGES.

Lib/test/test_iter.py
Misc/NEWS
Python/bltinmodule.c

index 66f40c1571c358b7bb98ced11cb73f80cb059c31..55845879a581c62b6aa43994375b0ffc03990805 100644 (file)
@@ -385,4 +385,17 @@ class TestCase(unittest.TestCase):
             except OSError:
                 pass
 
+    # Test reduces()'s use of iterators.
+    def test_builtin_reduce(self):
+        from operator import add
+        self.assertEqual(reduce(add, SequenceClass(5)), 10)
+        self.assertEqual(reduce(add, SequenceClass(5), 42), 52)
+        self.assertRaises(TypeError, reduce, add, SequenceClass(0))
+        self.assertEqual(reduce(add, SequenceClass(0), 42), 42)
+        self.assertEqual(reduce(add, SequenceClass(1)), 0)
+        self.assertEqual(reduce(add, SequenceClass(1), 42), 42)
+
+        d = {"one": 1, "two": 2, "three": 3}
+        self.assertEqual(reduce(add, d), "".join(d.keys()))
+
 run_unittest(TestCase)
index 617146c9719b95fd79a6ed634a32fd5e64ecd1ff..d707a7acd9647add27ad19bea2b8cc437745a1e3 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -22,6 +22,7 @@ Core
     map()
     max()
     min()
+    reduce()
 
 
 What's New in Python 2.1 (final)?
index 0c20d1005892d5ef0886b041b4bcf400024e93c7..5209607df154d1db00cf90c356ddf5b0442affd7 100644 (file)
@@ -1851,26 +1851,25 @@ is printed without a trailing newline before reading.";
 static PyObject *
 builtin_reduce(PyObject *self, PyObject *args)
 {
-       PyObject *seq, *func, *result = NULL;
-       PySequenceMethods *sqf;
-       register int i;
+       PyObject *seq, *func, *result = NULL, *it;
 
        if (!PyArg_ParseTuple(args, "OO|O:reduce", &func, &seq, &result))
                return NULL;
        if (result != NULL)
                Py_INCREF(result);
 
-       sqf = seq->ob_type->tp_as_sequence;
-       if (sqf == NULL || sqf->sq_item == NULL) {
+       it = PyObject_GetIter(seq);
+       if (it == NULL) {
                PyErr_SetString(PyExc_TypeError,
-                   "reduce() arg 2 must be a sequence");
+                   "reduce() arg 2 must support iteration");
+               Py_XDECREF(result);
                return NULL;
        }
 
        if ((args = PyTuple_New(2)) == NULL)
                goto Fail;
 
-       for (i = 0; ; ++i) {
+       for (;;) {
                PyObject *op2;
 
                if (args->ob_refcnt > 1) {
@@ -1879,12 +1878,18 @@ builtin_reduce(PyObject *self, PyObject *args)
                                goto Fail;
                }
 
-               if ((op2 = (*sqf->sq_item)(seq, i)) == NULL) {
-                       if (PyErr_ExceptionMatches(PyExc_IndexError)) {
-                               PyErr_Clear();
-                               break;
+               op2 = PyIter_Next(it);
+               if (op2 == NULL) {
+                       /* StopIteration is *implied* by a NULL return from
+                        * PyIter_Next() if PyErr_Occurred() is false.
+                        */
+                       if (PyErr_Occurred()) {
+                               if (PyErr_ExceptionMatches(PyExc_StopIteration))
+                                       PyErr_Clear();
+                               else
+                                       goto Fail;
                        }
-                       goto Fail;
+                       break;
                }
 
                if (result == NULL)
@@ -1903,11 +1908,13 @@ builtin_reduce(PyObject *self, PyObject *args)
                PyErr_SetString(PyExc_TypeError,
                           "reduce() of empty sequence with no initial value");
 
+       Py_DECREF(it);
        return result;
 
 Fail:
        Py_XDECREF(args);
        Py_XDECREF(result);
+       Py_DECREF(it);
        return NULL;
 }