Generalize reduce() to work with iterators.

NEEDS DOC CHANGES.
This commit is contained in:
Tim Peters 2001-05-04 04:39:21 +00:00
parent 8bc10b0c57
commit 15d81efb8a
3 changed files with 33 additions and 12 deletions

View file

@ -385,4 +385,17 @@ class TestCase(unittest.TestCase):
except OSError: except OSError:
pass 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) run_unittest(TestCase)

View file

@ -22,6 +22,7 @@ Core
map() map()
max() max()
min() min()
reduce()
What's New in Python 2.1 (final)? What's New in Python 2.1 (final)?

View file

@ -1851,26 +1851,25 @@ is printed without a trailing newline before reading.";
static PyObject * static PyObject *
builtin_reduce(PyObject *self, PyObject *args) builtin_reduce(PyObject *self, PyObject *args)
{ {
PyObject *seq, *func, *result = NULL; PyObject *seq, *func, *result = NULL, *it;
PySequenceMethods *sqf;
register int i;
if (!PyArg_ParseTuple(args, "OO|O:reduce", &func, &seq, &result)) if (!PyArg_ParseTuple(args, "OO|O:reduce", &func, &seq, &result))
return NULL; return NULL;
if (result != NULL) if (result != NULL)
Py_INCREF(result); Py_INCREF(result);
sqf = seq->ob_type->tp_as_sequence; it = PyObject_GetIter(seq);
if (sqf == NULL || sqf->sq_item == NULL) { if (it == NULL) {
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"reduce() arg 2 must be a sequence"); "reduce() arg 2 must support iteration");
Py_XDECREF(result);
return NULL; return NULL;
} }
if ((args = PyTuple_New(2)) == NULL) if ((args = PyTuple_New(2)) == NULL)
goto Fail; goto Fail;
for (i = 0; ; ++i) { for (;;) {
PyObject *op2; PyObject *op2;
if (args->ob_refcnt > 1) { if (args->ob_refcnt > 1) {
@ -1879,12 +1878,18 @@ builtin_reduce(PyObject *self, PyObject *args)
goto Fail; goto Fail;
} }
if ((op2 = (*sqf->sq_item)(seq, i)) == NULL) { op2 = PyIter_Next(it);
if (PyErr_ExceptionMatches(PyExc_IndexError)) { if (op2 == NULL) {
PyErr_Clear(); /* StopIteration is *implied* by a NULL return from
break; * 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) if (result == NULL)
@ -1903,11 +1908,13 @@ builtin_reduce(PyObject *self, PyObject *args)
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"reduce() of empty sequence with no initial value"); "reduce() of empty sequence with no initial value");
Py_DECREF(it);
return result; return result;
Fail: Fail:
Py_XDECREF(args); Py_XDECREF(args);
Py_XDECREF(result); Py_XDECREF(result);
Py_DECREF(it);
return NULL; return NULL;
} }