[Python-checkins] python/nondist/sandbox/itertools itertools.c,NONE,1.1 libitertools.tex,NONE,1.1 setup.py,NONE,1.1 test_itertools.py,NONE,1.1

rhettinger@users.sourceforge.net rhettinger@users.sourceforge.net
Sun, 26 Jan 2003 16:16:53 -0800


Update of /cvsroot/python/python/nondist/sandbox/itertools
In directory sc8-pr-cvs1:/tmp/cvs-serv10778/itertools

Added Files:
	itertools.c libitertools.tex setup.py test_itertools.py 
Log Message:
Open a sandbox directory for itertools which provide SML/Haskell style
tools creating iterators and looping.


--- NEW FILE: itertools.c ---

#include "Python.h"

/* times object ************************************************************/

typedef struct {
        PyObject_HEAD
        long    cnt;
} timesobject;

PyTypeObject times_type;

static PyObject *
times_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
        timesobject *lz;
        long cnt;

        if (!PyArg_ParseTuple(args, "l:times", &cnt))
                return NULL;

        /* create timesobject structure */
        lz = (timesobject *)PyObject_New(timesobject, &times_type);
        if (lz == NULL)
                return NULL;
        lz->cnt = cnt;

        return (PyObject *)lz;
}

static PyObject *
times_next(timesobject *lz)
{
	if (lz->cnt > 0) {
		lz->cnt--;
		Py_INCREF(Py_None);
		return Py_None;
	}
	return NULL;
}

static PyObject *
times_getiter(PyObject *lz)
{
        Py_INCREF(lz);
        return lz;
}

PyDoc_STRVAR(times_doc,
"times(n) --> times object\n\
\n\
Return an times object whose .next() method returns n consecutive\n\
instance of None.");

PyTypeObject times_type = {
        PyObject_HEAD_INIT(NULL)
        0,                              /* ob_size */
        "itertools.times",             /* tp_name */
        sizeof(timesobject),           /* tp_basicsize */
        0,                              /* tp_itemsize */
        /* methods */
        (destructor)PyObject_Del,       /* tp_dealloc */
        0,                              /* tp_print */
        0,                              /* tp_getattr */
        0,                              /* tp_setattr */
        0,                              /* tp_compare */
        0,                              /* tp_repr */
        0,                              /* tp_as_number */
        0,                              /* tp_as_sequence */
        0,                              /* tp_as_mapping */
        0,                              /* tp_hash */
        0,                              /* tp_call */
        0,                              /* tp_str */
        PyObject_GenericGetAttr,        /* tp_getattro */
        0,                              /* tp_setattro */
        0,                              /* tp_as_buffer */
        Py_TPFLAGS_DEFAULT,             /* tp_flags */
        times_doc,                       /* tp_doc */
        0,                              /* tp_traverse */
        0,                              /* tp_clear */
        0,                              /* tp_richcompare */
        0,                              /* tp_weaklistoffset */
        (getiterfunc)times_getiter,      /* tp_iter */
        (iternextfunc)times_next,        /* tp_iternext */
        0,                              /* tp_methods */
        0,                              /* tp_members */
        0,                              /* tp_getset */
        0,                              /* tp_base */
        0,                              /* tp_dict */
        0,                              /* tp_descr_get */
        0,                              /* tp_descr_set */
        0,                              /* tp_dictoffset */
        0,                              /* tp_init */
        PyType_GenericAlloc,            /* tp_alloc */
        times_new,                      /* tp_new */
};


/* ifilter object ************************************************************/

typedef struct {
        PyObject_HEAD
        PyObject *func;
        PyObject *it;
        long     invert;
} ifilterobject;

PyTypeObject ifilter_type;

static PyObject *
ifilter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
        PyObject *func, *seq, *invert=NULL;
        PyObject *it;
        ifilterobject *lz;
        long inv=0;

        if (!PyArg_UnpackTuple(args, "ifilter", 2, 3, &func, &seq, &invert))
                return NULL;

        if (invert != NULL  &&  PyObject_IsTrue(invert))
                inv = 1;

        /* Get iterator. */
        it = PyObject_GetIter(seq);
        if (it == NULL)
                return NULL;

        /* create ifilterobject structure */
        lz = (ifilterobject *)type->tp_alloc(type, 0);
        if (lz == NULL) {
                Py_DECREF(it);
                return NULL;
        }
        lz->func = func;
        lz->it = it;
        lz->invert = inv;

        return (PyObject *)lz;
}

static void
ifilter_dealloc(ifilterobject *lz)
{
        PyObject_GC_UnTrack(lz);
        //Py_XDECREF(lz->func);
        Py_XDECREF(lz->it);
        lz->ob_type->tp_free(lz);
}

static int
ifilter_traverse(ifilterobject *lz, visitproc visit, void *arg)
{
        if (lz->it)
                return visit(lz->it, arg);
        return 0;
}

static PyObject *
ifilter_next(ifilterobject *lz)
{
        PyObject *item;
        long ok;

        for (;;) {
                item = PyIter_Next(lz->it);
                if (item == NULL)
                        return NULL;

                if (lz->func == Py_None) {
                        ok = PyObject_IsTrue(item);
                }
                else {
                        PyObject *good;
                        good = PyObject_CallFunctionObjArgs(lz->func, item, NULL);
                        if (good == NULL) {
                                Py_DECREF(item);
                                return NULL;
                        }
                        ok = PyObject_IsTrue(good);
                        Py_DECREF(good);
                }
                if (ok ^ lz->invert)
                        return item;
                Py_DECREF(item);
        }
}

static PyObject *
ifilter_getiter(PyObject *lz)
{
        Py_INCREF(lz);
        return lz;
}

PyDoc_STRVAR(ifilter_doc,
"filter(function or None, sequence [, invert]) --> ifilter object\n\
\n\
Return those items of sequence for which function(item) is true.  If\n\
invert is set to True, return items for which function(item) if False.\n\
If function is None, return the items that are true (unless invert is set).");

PyTypeObject ifilter_type = {
        PyObject_HEAD_INIT(NULL)
        0,                              /* ob_size */
        "itertools.ifilter",             /* tp_name */
        sizeof(ifilterobject),           /* tp_basicsize */
        0,                              /* tp_itemsize */
        /* methods */
        (destructor)ifilter_dealloc,       /* tp_dealloc */
        0,                              /* tp_print */
        0,                              /* tp_getattr */
        0,                              /* tp_setattr */
        0,                              /* tp_compare */
        0,                              /* tp_repr */
        0,                              /* tp_as_number */
        0,                              /* tp_as_sequence */
        0,                              /* tp_as_mapping */
        0,                              /* tp_hash */
        0,                              /* tp_call */
        0,                              /* tp_str */
        PyObject_GenericGetAttr,        /* tp_getattro */
        0,                              /* tp_setattro */
        0,                              /* tp_as_buffer */
        Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
                Py_TPFLAGS_BASETYPE,    /* tp_flags */
        ifilter_doc,                       /* tp_doc */
        (traverseproc)ifilter_traverse,    /* tp_traverse */
        0,                              /* tp_clear */
        0,                              /* tp_richcompare */
        0,                              /* tp_weaklistoffset */
        (getiterfunc)ifilter_getiter,      /* tp_iter */
        (iternextfunc)ifilter_next,        /* tp_iternext */
        0,                              /* tp_methods */
        0,                              /* tp_members */
        0,                              /* tp_getset */
        0,                              /* tp_base */
        0,                              /* tp_dict */
        0,                              /* tp_descr_get */
        0,                              /* tp_descr_set */
        0,                              /* tp_dictoffset */
        0,                              /* tp_init */
        PyType_GenericAlloc,            /* tp_alloc */
        ifilter_new,                     /* tp_new */
        PyObject_GC_Del,                /* tp_free */
};


/* count object ************************************************************/

typedef struct {
        PyObject_HEAD
        long    cnt;
} countobject;

PyTypeObject count_type;

static PyObject *
count_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
        countobject *lz;
        long cnt = 0;

        if (!PyArg_ParseTuple(args, "|l:count", &cnt))
                return NULL;

        /* create countobject structure */
        lz = (countobject *)PyObject_New(countobject, &count_type);
        if (lz == NULL)
                return NULL;
        lz->cnt = cnt;

        return (PyObject *)lz;
}

static PyObject *
count_next(countobject *lz)
{
        return PyInt_FromLong(lz->cnt++);
}

static PyObject *
count_getiter(PyObject *lz)
{
        Py_INCREF(lz);
        return lz;
}

PyDoc_STRVAR(count_doc,
"count([firstval]) --> count object\n\
\n\
Return an count object whose .next() method returns consecutive\n\
integers starting from zero or, if specified, from firstval.");

PyTypeObject count_type = {
        PyObject_HEAD_INIT(NULL)
        0,                              /* ob_size */
        "itertools.count",             /* tp_name */
        sizeof(countobject),           /* tp_basicsize */
        0,                              /* tp_itemsize */
        /* methods */
        (destructor)PyObject_Del,       /* tp_dealloc */
        0,                              /* tp_print */
        0,                              /* tp_getattr */
        0,                              /* tp_setattr */
        0,                              /* tp_compare */
        0,                              /* tp_repr */
        0,                              /* tp_as_number */
        0,                              /* tp_as_sequence */
        0,                              /* tp_as_mapping */
        0,                              /* tp_hash */
        0,                              /* tp_call */
        0,                              /* tp_str */
        PyObject_GenericGetAttr,        /* tp_getattro */
        0,                              /* tp_setattro */
        0,                              /* tp_as_buffer */
        Py_TPFLAGS_DEFAULT,             /* tp_flags */
        count_doc,                       /* tp_doc */
        0,                              /* tp_traverse */
        0,                              /* tp_clear */
        0,                              /* tp_richcompare */
        0,                              /* tp_weaklistoffset */
        (getiterfunc)count_getiter,      /* tp_iter */
        (iternextfunc)count_next,        /* tp_iternext */
        0,                              /* tp_methods */
        0,                              /* tp_members */
        0,                              /* tp_getset */
        0,                              /* tp_base */
        0,                              /* tp_dict */
        0,                              /* tp_descr_get */
        0,                              /* tp_descr_set */
        0,                              /* tp_dictoffset */
        0,                              /* tp_init */
        PyType_GenericAlloc,            /* tp_alloc */
        count_new,                      /* tp_new */
};


/* loopzip object ************************************************************/

#include "Python.h"

typedef struct {
        PyObject_HEAD
        long    tuplesize;
        PyObject *ittuple;              /* tuple of iterators */
        PyObject *result;
} loopzipobject;

PyTypeObject loopzip_type;

static PyObject *
loopzip_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
        loopzipobject *lz;
        int i;
        PyObject *ittuple;  /* tuple of iterators */
        int tuplesize = PySequence_Length(args);

        if (tuplesize < 1) {
                PyErr_SetString(PyExc_TypeError,
                                "loopzip() requires at least one sequence");
                return NULL;
        }

        /* args must be a tuple */
        assert(PyTuple_Check(args));

        /* obtain iterators */
        ittuple = PyTuple_New(tuplesize);
        if(ittuple == NULL)
                return NULL;
        for (i = 0; i < tuplesize; ++i) {
                PyObject *item = PyTuple_GET_ITEM(args, i);
                PyObject *it = PyObject_GetIter(item);
                if (it == NULL) {
                        if (PyErr_ExceptionMatches(PyExc_TypeError))
                                PyErr_Format(PyExc_TypeError,
                                    "loopzip argument #%d must support iteration",
                                    i+1);
                        Py_DECREF(ittuple);
                        return NULL;
                }
                PyTuple_SET_ITEM(ittuple, i, it);
        }

        /* create loopzipobject structure */
        lz = (loopzipobject *)type->tp_alloc(type, 0);
        if (lz == NULL) {
                Py_DECREF(ittuple);
                return NULL;
        }
        lz->ittuple = ittuple;
        lz->tuplesize = tuplesize;

        /* create result holder */
        lz->result = PyList_New(tuplesize);
        if (lz->result == NULL) {
                Py_DECREF(ittuple);
                Py_DECREF(lz);
                return NULL;
        }
        for (i=0 ; i < tuplesize ; i++) {
                Py_INCREF(Py_None);
                PyList_SET_ITEM(lz->result, i, Py_None);
        }

        return (PyObject *)lz;
}

static void
loopzip_dealloc(loopzipobject *lz)
{
        PyObject_GC_UnTrack(lz);
        Py_XDECREF(lz->ittuple);
        //Py_XDECREF(lz->result);
        lz->ob_type->tp_free(lz);
}

static int
loopzip_traverse(loopzipobject *lz, visitproc visit, void *arg)
{
        if (lz->ittuple)
                return visit(lz->ittuple, arg);
        return 0;
}

static PyObject *
loopzip_next(loopzipobject *lz)
{
        int i;
        long tuplesize = lz->tuplesize;
        PyObject *result = lz->result;
        PyObject *it;
        PyObject *item;

        /* XXX: Add check that resultsize == tuplesize */
        for (i=0 ; i < tuplesize ; i++) {
                item = PyList_GET_ITEM(result, i);
                Py_DECREF(item);
                it = PyTuple_GET_ITEM(lz->ittuple, i);
                item = PyIter_Next(it);
                if (item == NULL)
                        return NULL;
                PyList_SET_ITEM(result, i, item);
        }
        Py_INCREF(result);
        return result;
}

static PyObject *
loopzip_getiter(PyObject *lz)
{
        Py_INCREF(lz);
        return lz;
}

PyDoc_STRVAR(loopzip_doc,
"loopzip(iter1 [,iter2 [...]]) --> loopzip object\n\
\n\
Return an loopzip object whose .next() method returns a list where\n\
the i-th element comes from the i-th iterable argument.  The .next()\n\
method updates the returns the same list everytime until the shortest\n\
iterable in the argument sequence is exhausted and then it raises\n\
StopIteration.  Works like the zip() function but consumes less memory.\n\
Unlike zip, it returns an iterator and the n-th return is a list rather\n\
than a tuple.  It is appropriate for use in loops, but not for conversion\n\
to a list.  For example:  list(loopzip('abc')) returns a list of three\n\
identical sublists which is usually not what was intended.");

PyTypeObject loopzip_type = {
        PyObject_HEAD_INIT(NULL)
        0,                              /* ob_size */
        "itertools.loopzip",             /* tp_name */
        sizeof(loopzipobject),           /* tp_basicsize */
        0,                              /* tp_itemsize */
        /* methods */
        (destructor)loopzip_dealloc,       /* tp_dealloc */
        0,                              /* tp_print */
        0,                              /* tp_getattr */
        0,                              /* tp_setattr */
        0,                              /* tp_compare */
        0,                              /* tp_repr */
        0,                              /* tp_as_number */
        0,                              /* tp_as_sequence */
        0,                              /* tp_as_mapping */
        0,                              /* tp_hash */
        0,                              /* tp_call */
        0,                              /* tp_str */
        PyObject_GenericGetAttr,        /* tp_getattro */
        0,                              /* tp_setattro */
        0,                              /* tp_as_buffer */
        Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
                Py_TPFLAGS_BASETYPE,    /* tp_flags */
        loopzip_doc,                       /* tp_doc */
        (traverseproc)loopzip_traverse,    /* tp_traverse */
        0,                              /* tp_clear */
        0,                              /* tp_richcompare */
        0,                              /* tp_weaklistoffset */
        (getiterfunc)loopzip_getiter,      /* tp_iter */
        (iternextfunc)loopzip_next,        /* tp_iternext */
        0,                              /* tp_methods */
        0,                              /* tp_members */
        0,                              /* tp_getset */
        0,                              /* tp_base */
        0,                              /* tp_dict */
        0,                              /* tp_descr_get */
        0,                              /* tp_descr_set */
        0,                              /* tp_dictoffset */
        0,                              /* tp_init */
        PyType_GenericAlloc,            /* tp_alloc */
        loopzip_new,                     /* tp_new */
        PyObject_GC_Del,                /* tp_free */
};


/* repeat object ************************************************************/

typedef struct {
        PyObject_HEAD
        PyObject *element;
} repeatobject;

PyTypeObject repeat_type;

static PyObject *
repeat_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
        repeatobject *ro;
        PyObject *element;

        if (!PyArg_UnpackTuple(args, "repeat", 1, 1, &element))
                return NULL;

        ro = (repeatobject *)type->tp_alloc(type, 0);
        if (ro == NULL)
                return NULL;
        Py_INCREF(element);
        ro->element = element;
        return (PyObject *)ro;
}

static void
repeat_dealloc(repeatobject *ro)
{
        PyObject_GC_UnTrack(ro);
        Py_XDECREF(ro->element);
        ro->ob_type->tp_free(ro);
}

static int
repeat_traverse(repeatobject *ro, visitproc visit, void *arg)
{
        if (ro->element)
                return visit(ro->element, arg);
        return 0;
}

static PyObject *
repeat_next(repeatobject *ro)
{
        Py_INCREF(ro->element);
        return ro->element;
}

static PyObject *
repeat_getiter(PyObject *ro)
{
        Py_INCREF(ro);
        return ro;
}

PyDoc_STRVAR(repeat_doc,
"repeat(element) -> create an iterator which returns the element endlessly.");

PyTypeObject repeat_type = {
        PyObject_HEAD_INIT(NULL)
        0,                              /* ob_size */
        "itertools.repeat",             /* tp_name */
        sizeof(repeatobject),           /* tp_basicsize */
        0,                              /* tp_itemsize */
        /* methods */
        (destructor)repeat_dealloc,       /* tp_dealloc */
        0,                              /* tp_print */
        0,                              /* tp_getattr */
        0,                              /* tp_setattr */
        0,                              /* tp_compare */
        0,                              /* tp_repr */
        0,                              /* tp_as_number */
        0,                              /* tp_as_sequence */
        0,                              /* tp_as_mapping */
        0,                              /* tp_hash */
        0,                              /* tp_call */
        0,                              /* tp_str */
        PyObject_GenericGetAttr,        /* tp_getattro */
        0,                              /* tp_setattro */
        0,                              /* tp_as_buffer */
        Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
                Py_TPFLAGS_BASETYPE,    /* tp_flags */
        repeat_doc,                       /* tp_doc */
        (traverseproc)repeat_traverse,    /* tp_traverse */
        0,                              /* tp_clear */
        0,                              /* tp_richcompare */
        0,                              /* tp_weaklistoffset */
        (getiterfunc)repeat_getiter,      /* tp_iter */
        (iternextfunc)repeat_next,        /* tp_iternext */
        0,                              /* tp_methods */
        0,                              /* tp_members */
        0,                              /* tp_getset */
        0,                              /* tp_base */
        0,                              /* tp_dict */
        0,                              /* tp_descr_get */
        0,                              /* tp_descr_set */
        0,                              /* tp_dictoffset */
        0,                              /* tp_init */
        PyType_GenericAlloc,            /* tp_alloc */
        repeat_new,                     /* tp_new */
        PyObject_GC_Del,                /* tp_free */
};


/* module level code ********************************************************/

PyDoc_STRVAR(module_doc,
"Module implements a set of functional tools for creating and using iterators.\n\
\n\
Infinite iterators:\n\
count([n]) --> n, n+1, n+2, ...\n\
cycle(seq) --> seq0, seq1, ... seqlast, seq0, ...\n\
repeat(elem) --> elem, elem, elem, elem\n\
tabulate(fun, [n]) --> fun(n), fun(n+1), fun(n+2), ...\n\
\n\
Iterators terminating on the shortest input sequence:\n\
loopzip(p, q, ...) --> [p[0], q[0]], [p[1], q[1]], ... \n\
       same list each time but with updated contents\n\
ifilter(pred, seq, invert=False) --> elements of seq where\n\
       pred(elem) is True (or False invert is set)\n\
islice(seq, start, stop, step) --> elements from\n\
       seq[start:stop:step]\n\
imap(fun, p, q) --> fun(p0, q0), fun(p1, q1), ...\n\
");

/*
XXX
repeat(e) = lambda: cycle([e])
enumerate(s, n=0) = lambda: izip(count(n), s)
tabulate(f, n=0) = lambda: imap(f, count(n))
nth(s, n) = lambda: islice(n, n+1)

? apply(s, f, args, kw)

? are both izip and loopzip needed?
? first(pred, seqn)

takewhile
dropwhile

No iterator form: partition(s, pred)

unzip --> multiple iterators   ?? how would this work

starmap --> [yield func(*args) for args in ia]
starmap(f, s) --> f(*(s1)), f(*(s2))
*/

PyMODINIT_FUNC
inititertools(void)
{
        PyObject *m;
        m = Py_InitModule3("itertools", NULL, module_doc);

        if (PyType_Ready(&times_type) < 0)
                return;
        Py_INCREF(&times_type);
        PyModule_AddObject(m, "times", (PyObject *)&times_type);

        if (PyType_Ready(&ifilter_type) < 0)
                return;
        Py_INCREF(&ifilter_type);
        PyModule_AddObject(m, "ifilter", (PyObject *)&ifilter_type);

        if (PyType_Ready(&count_type) < 0)
                return;
        Py_INCREF(&count_type);
        PyModule_AddObject(m, "count", (PyObject *)&count_type);

        if (PyType_Ready(&loopzip_type) < 0)
                return;
        Py_INCREF(&loopzip_type);
        PyModule_AddObject(m, "loopzip", (PyObject *)&loopzip_type);

        if (PyType_Ready(&repeat_type) < 0)
                return;
        Py_INCREF(&repeat_type);
        PyModule_AddObject(m, "repeat", (PyObject *)&repeat_type);
}

--- NEW FILE: libitertools.tex ---
\section{\module{itertools} ---
         Functions creating iterators for efficient looping}

\declaremodule{standard}{itertools}
\modulesynopsis{Functions creating iterators for efficient looping.}


This module implements a number of iterator building blocks inspired
by constructs from the Haskell and SML programming languages.  Each
has been recast in a form suitable for Python.
\versionadded{2.3}

With the advent of iterators and generators in Python 2.3, each of
these tools can be expressed easily and succinctly in pure python.
Rather duplicating what can already be done, this module takes the
slightly unnatural approach of providing value in other ways:

\begin{itemize}

    \item Instead of constructing an over-specialized toolset, this module
        provides basic building blocks that can be readily combined.

        For instance, SML provides a tabulation tool: \code{tabulate(\var{f})}
        which produces a sequence \code{f(0), f(1), ...}.  This toolbox
        takes a diffenct approach of providing \function{imap()} and
        \function{count()} which can be combined to form
        \code{imap(\var{f}, count())} and produce an equivalent result.

    \item Wherever straight-forward alternatives exist, the corresponding
        tools in this module seek to meet a different need and are designed
        for speed.  In fact, the \emph{sole} justification for this module
        being written in C is its speed advantage.

        For instance, Python's
        \module{__builtins__} module has an easy-to-use, no surprises version
        of \function(zip()).  This module's corresponding function,
        \function{loopzip()} returns an iterator rather than a full list.
        Also, calls to the iterator return a mutable list rather than a tuple
        and it returns the \emph{same} list on each pass.  Used in a
        \keyword{for} loop, \function{loopzip()} can be directly substituted
        for \function{zip()} and run much faster.  It has nearly zero
        overhead since the looping is done in C code (bypassing Python's eval
	loop); since it returns an iterator (saving the need to allocate a
	list and append to it an element at a time); and since it reuses just
        one output list (saving the time to allocate and build a tuple on
	every pass).  Though very fast, using \function{loopzip()} outside of
        a \keyword{for} loop can result in surprising behavior and an
        unwelcome refresher lesson in mutability.

    \item Another source of value comes from standardizing a core set of tools
        to avoid the readability and reliability problems that arise when many
        different individuals create their own slightly varying implementations
        each with their own quirks and naming conventions.

    \item Whether cast in pure python form or C code, tools that use iterators
        are more memory efficient than their list based counterparts.
        Adopting the principles of just-in-time manufacturing, they create
        data when and where needed instead of consuming memory with the
        computer equivalent of ``inventory''.

\end{itemize}    


\subsection{Itertool functions \label{itertools-functions}}


\begin{funcdesc}{count}{\optional{n}}
  Make an iterator that returns consecutive integers starting with \var{n}.
  Used in zip and loop constructions to consecutively number a sequence.
  Equivalent to:

  \begin{verbatim}
     def count(n=0):
         while True:
             yield n
             cnt += 1
  \end{verbatim}
\end{funcdesc}

\begin{funcdesc}{ifilter}{func, iterable \optional{, invert}}
  Make an iterator that filters elements from iterable returning only
  those for which the function evaluates to \code{True}.  If \var{invert}
  is \code{True}, then reverse the process and pass through only those
  elements for which the function evaluates to \code{False}.
  Equivalent to:

  \begin{verbatim}
     def filter(func, iterable, invert=False):
	 iterable = iter(iterable)
         while True:
	     x = bool(iterable.next())
	     if not invert and x or invert and not x:
		 yield None
  \end{verbatim}
\end{funcdesc}

\begin{funcdesc}{loopzip}{*iterables}
  Make an iterator that aggregates elements from each of the iterables.
  Like \function{zip()} except that it returns an iterator instead of
  a list and the individual elements are stored in a list rather than
  in a tuple.  The \emph{same} list is used for each pass and only the
  contents are updated; hence, \function{loopzip()} is only appropriate
  in a \keyword{for} loop or other itertool.  The iterator terminates
  with \exception{StopIteration} when the first of the iterables is
  exhausted.  Equivalent to:

  \begin{verbatim}
     def loopzip(*iterables):
	 iterables = map(iter, iterables)
	 result = [None] * len(iterables)
         while True:
	     for i in xrange(len(iterables)):
		 result[i] = iterable[i].next()
	     yield result
  \end{verbatim}
\end{funcdesc}

\begin{funcdesc}{repeat}{obj}
  Make an iterator that returns \var{obj} over and over again.
  Used inside map, zip, or loop constructions for multiple
  references to the same object.  Equivalent to:

  \begin{verbatim}
     def repeat(x):
         while True:
             yield None
  \end{verbatim}
\end{funcdesc}

\begin{funcdesc}{times}{n}
  Make an iterator that returns None \var{n} times.
  Used for looping a specific number of times without creating a number object
  on each pass.  Equivalent to:

  \begin{verbatim}
     def times(n):
         for i in xrange(n):
             yield None
  \end{verbatim}
\end{funcdesc}


\subsection{Examples \label{itertools-example}}

The following examples show common uses for each tool and
demonstrate ways they can be combined.

\begin{verbatim}
>>> for i in times(3):
...     print "Hello"
...
Hello
Hello
Hello

>>> for checknum, amount in loopzip(count(1200), amounts):
...     print 'Check %d is for $%.2f' % (checknum, amount)
...
Check 1200 is for $120.15
Check 1201 is for $764.05
Check 1202 is for $823.14

>>> bases = [2, 3, 5, 7]
>>> powers = [2, 3, 4]
>>> for power in powers:
...     print list(imap(operator.pow, bases, repeat(power)))
...
\end{verbatim}                                                                                      

As a further example of how itertools can be combined, here
are a few re-definitions of existing tools:

\begin{verbatim}
>>> enumerate = lambda s: izip(count(), s)
>>> tabulate = lambda f: imap(f, count())
>>> iteritems = lambda d: izip(d.iterkeys(), d.itervalues())
>>> repeat = lambda o: cycle([o])

\end{verbatim}

--- NEW FILE: setup.py ---
from distutils.core import setup, Extension

setup(name="itertools",
      ext_modules=[Extension("itertools", ["itertools.c"])])

--- NEW FILE: test_itertools.py ---
import unittest
from test import test_support
from itertools import *

class TestBasicOps(unittest.TestCase):
    def test_count(self):
        self.assertEqual(zip('abc',count()), [('a', 0), ('b', 1), ('c', 2)])
        self.assertEqual(zip('abc',count(3)), [('a', 3), ('b', 4), ('c', 5)])

    def test_ifilter(self):
        def isEven(x):
            return x%2==0
        self.assertEqual(list(ifilter(isEven, range(6))), [0,2,4])
        self.assertEqual(list(ifilter(isEven, range(6), True)), [1,3,5])

    def test_loopzip(self):
        ans = [(x,y) for x, y in loopzip('abc',count())]
        self.assertEqual(ans, [('a', 0), ('b', 1), ('c', 2)])
        self.assertEqual(list(loopzip('abc',count())), [['c', 2]] * 3)

    def test_repeat(self):
        self.assertEqual(zip(xrange(3),repeat('a')), [(0, 'a'), (1, 'a'), (2, 'a')])

    def test_times(self):
        self.assertEqual(list(times(3)), [None]*3)


def test_main():
    suite = unittest.TestSuite()
    for testclass in (TestBasicOps,
                          ):
        suite.addTest(unittest.makeSuite(testclass))
    test_support.run_suite(suite)

if __name__ == "__main__":
    test_main()