[Python-checkins] python/nondist/sandbox/itertools itertools.c,1.4,1.5 libitertools.tex,1.5,1.6 test_itertools.py,1.3,1.4 todo.txt,1.5,1.6

rhettinger@users.sourceforge.net rhettinger@users.sourceforge.net
Mon, 27 Jan 2003 02:03:13 -0800


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

Modified Files:
	itertools.c libitertools.tex test_itertools.py todo.txt 
Log Message:
Added islice() and stronger error checking.

Index: itertools.c
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/itertools/itertools.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -C2 -d -r1.4 -r1.5
*** itertools.c	27 Jan 2003 05:24:40 -0000	1.4
--- itertools.c	27 Jan 2003 10:02:59 -0000	1.5
***************
*** 2,5 ****
--- 2,170 ----
  #include "Python.h"
  
+ /* islice object ************************************************************/
+ 
+ typedef struct {
+ 	PyObject_HEAD
+ 	PyObject *it;
+ 	long	next;
+ 	long	stop;
+ 	long	step;
+ 	long	cnt;
+ } isliceobject;
+ 
+ PyTypeObject islice_type;
+ 
+ static PyObject *
+ islice_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+ {
+ 	PyObject *seq;
+ 	long a1=0, a2=0, a3=0, start=0, stop=0, step=1;
+ 	PyObject *it;
+ 	int numargs;
+ 	isliceobject *lz;
+ 
+ 	numargs = PyTuple_Size(args);
+ 	if (!PyArg_ParseTuple(args, "Ol|ll:islice", &seq, &a1, &a2, &a3))
+ 		return NULL;
+ 
+ 	if (numargs == 2) {
+ 		stop = a1;
+ 	} else if (numargs == 3) {
+ 		start = a1;
+ 		stop = a2;
+ 	} else {
+ 		start = a1;
+ 		stop = a2;
+ 		step = a3;
+ 	}
+ 
+ 	if (start<0 || stop<0) {
+ 		PyErr_SetString(PyExc_ValueError,
+ 		   "Indices for islice() must be positive.");
+ 		return NULL;
+ 	}
+ 
+ 	if (step<1) {
+ 		PyErr_SetString(PyExc_ValueError,
+ 		   "Step must be one or larger for islice().");
+ 		return NULL;
+ 	}
+ 
+ 	/* Get iterator. */
+ 	it = PyObject_GetIter(seq);
+ 	if (it == NULL)
+ 		return NULL;
+ 
+ 	/* create isliceobject structure */
+ 	lz = (isliceobject *)type->tp_alloc(type, 0);
+ 	if (lz == NULL) {
+ 		Py_DECREF(it);
+ 		return NULL;
+ 	}
+ 	lz->it = it;
+ 	lz->next = start;
+ 	lz->stop = stop;
+ 	lz->step = step;
+ 	lz->cnt = 0L;
+ 
+ 	return (PyObject *)lz;
+ }
+ 
+ static void
+ islice_dealloc(isliceobject *lz)
+ {
+         PyObject_GC_UnTrack(lz);
+         Py_XDECREF(lz->it);
+         lz->ob_type->tp_free(lz);
+ }
+ 
+ static int
+ islice_traverse(isliceobject *lz, visitproc visit, void *arg)
+ {
+         if (lz->it)
+                 return visit(lz->it, arg);
+         return 0;
+ }
+ 
+ static PyObject *
+ islice_next(isliceobject *lz)
+ {
+ 	PyObject *item;
+ 
+ 	while (lz->cnt < lz->next) {
+ 		item = PyIter_Next(lz->it);
+ 		if (item == NULL)
+ 			return NULL;
+ 		Py_DECREF(item);
+ 		lz->cnt++;
+ 	}
+ 	if (lz->stop != 0 && lz->cnt >= lz->stop)
+ 		return NULL;
+ 	item = PyIter_Next(lz->it);
+ 	if (item == NULL)
+ 		return NULL;
+ 	lz->cnt += 1;
+ 	lz->next += lz->step;
+ 	return item;
+ }
+ 
+ static PyObject *
+ islice_getiter(PyObject *lz)
+ {
+         Py_INCREF(lz);
+         return lz;
+ }
+ 
+ PyDoc_STRVAR(islice_doc,
+ "islice(function, sequence) --> islice object\n\
+ \n\
+ Return an iterator whose values are returned from the function evaluated\n\
+ with a argument tuple taken from the given sequence.");
+ 
+ PyTypeObject islice_type = {
+         PyObject_HEAD_INIT(NULL)
+         0,                              /* ob_size */
+         "itertools.islice",             /* tp_name */
+         sizeof(isliceobject),           /* tp_basicsize */
+         0,                              /* tp_itemsize */
+         /* methods */
+         (destructor)islice_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 */
+         islice_doc,                       /* tp_doc */
+         (traverseproc)islice_traverse,    /* tp_traverse */
+         0,                              /* tp_clear */
+         0,                              /* tp_richcompare */
+         0,                              /* tp_weaklistoffset */
+         (getiterfunc)islice_getiter,      /* tp_iter */
+         (iternextfunc)islice_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 */
+         islice_new,                     /* tp_new */
+         PyObject_GC_Del,                /* tp_free */
+ };
+ 
  
  /* starmap object ************************************************************/
***************
*** 150,155 ****
  
  	numargs = PyTuple_Size(args);
! 	if (numargs == 0) {
! 		// XXX raise Err for not having a function arg
  		return NULL;
  	}
--- 315,321 ----
  
  	numargs = PyTuple_Size(args);
! 	if (numargs < 2) {
! 		PyErr_SetString(PyExc_TypeError,
! 		   "imap() must have at least two arguments.");
  		return NULL;
  	}
***************
*** 305,308 ****
--- 471,480 ----
                  return NULL;
  
+ 	if (cnt < 0) {
+ 		PyErr_SetString(PyExc_ValueError,
+ 		   "count for imap() cannot be negative.");
+ 		return NULL;
+ 	}
+ 
          /* create timesobject structure */
          lz = (timesobject *)PyObject_New(timesobject, &times_type);
***************
*** 933,936 ****
--- 1105,1113 ----
          PyObject *m;
          m = Py_InitModule3("itertools", NULL, module_doc);
+ 
+         PyModule_AddObject(m, "islice", (PyObject *)&islice_type);
+         if (PyType_Ready(&islice_type) < 0)
+                 return;
+         Py_INCREF(&islice_type);
  
          PyModule_AddObject(m, "starmap", (PyObject *)&starmap_type);

Index: libitertools.tex
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/itertools/libitertools.tex,v
retrieving revision 1.5
retrieving revision 1.6
diff -C2 -d -r1.5 -r1.6
*** libitertools.tex	27 Jan 2003 05:24:40 -0000	1.5
--- libitertools.tex	27 Jan 2003 10:03:04 -0000	1.6
***************
*** 123,126 ****
--- 123,148 ----
  \end{funcdesc}
  
+ \begin{funcdesc}{islice}{iterable, \optional{start,} stop \optional{, step}}
+   Make an iterator that returns selected elements from the iterable.
+   If \var{start} is non-zero, then elements from the iterable are skiped
+   until start is reached.  Afterward, elements are returned consecutively
+   unless \var{step} is set higher than one which results in items being
+   skipped.  If \var{stop} is specified, then iteration stops at the
+   specified element position; otherwise, it continues indefinitely or
+   until the iterable is exhausted.  Unlike regular slicing,
+   \function{islice()} does not support negative values for \var{start},
+   \var{stop}, or \var{step}.  Equivalent to:
+ 
+   \begin{verbatim}
+      def islice(func, iterable, *args):
+          i = 0
+          for j in xrange(*args):
+              while i != j:
+                  _ = iterable.next()
+                  i += 1
+              yield iterable.next()
+   \end{verbatim}
+ \end{funcdesc}
+ 
  \begin{funcdesc}{loopzip}{*iterables}
    Make an iterator that aggregates elements from each of the iterables.
***************
*** 170,175 ****
  \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}
--- 192,197 ----
  \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}

Index: test_itertools.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/itertools/test_itertools.py,v
retrieving revision 1.3
retrieving revision 1.4
diff -C2 -d -r1.3 -r1.4
*** test_itertools.py	27 Jan 2003 05:24:40 -0000	1.3
--- test_itertools.py	27 Jan 2003 10:03:06 -0000	1.4
***************
*** 7,10 ****
--- 7,11 ----
          self.assertEqual(zip('abc',count()), [('a', 0), ('b', 1), ('c', 2)])
          self.assertEqual(zip('abc',count(3)), [('a', 3), ('b', 4), ('c', 5)])
+         self.assertRaises(TypeError, count, 2, 3)
  
      def test_ifilter(self):
***************
*** 13,16 ****
--- 14,21 ----
          self.assertEqual(list(ifilter(isEven, range(6))), [0,2,4])
          self.assertEqual(list(ifilter(isEven, range(6), True)), [1,3,5])
+         self.assertRaises(TypeError, ifilter)
+         self.assertRaises(TypeError, ifilter, 3)
+         self.assertRaises(TypeError, ifilter, isEven, 3)
+         self.assertRaises(TypeError, ifilter, isEven, [3], True, 4)
  
      def test_loopzip(self):
***************
*** 18,28 ****
--- 23,36 ----
          self.assertEqual(ans, [('a', 0), ('b', 1), ('c', 2)])
          self.assertEqual(list(loopzip('abc',count())), [['c', 2]] * 3)
+         self.assertRaises(TypeError, loopzip)
  
      def test_repeat(self):
          self.assertEqual(zip(xrange(3),repeat('a')),
                           [(0, 'a'), (1, 'a'), (2, 'a')])
+         self.assertRaises(TypeError, repeat)
  
      def test_times(self):
          self.assertEqual(list(times(3)), [None]*3)
+         self.assertRaises(ValueError, times, -1)
  
      def test_imap(self):
***************
*** 30,33 ****
--- 38,43 ----
          self.assertEqual(list(imap(operator.pow, range(3), range(1,7))),
                           [0**1, 1**2, 2**3])
+         self.assertRaises(TypeError, imap)
+         self.assertRaises(TypeError, imap, operator.neg)
  
      def test_starmap(self):
***************
*** 36,39 ****
--- 46,73 ----
                           [0**1, 1**2, 2**3])
  
+     def test_islice(self):
+         for args in [          # islice(args) should agree with range(args)
+                 (10, 20, 3),
+                 (10, 3, 20),
+                 (10, 20),
+                 (10, 3),
+                 (20,)
+                 ]:
+             self.assertEqual(list(islice(xrange(100), *args)), range(*args))
+ 
+         for args, tgtargs in [  # Stop when seqn is exhausted
+                 ((10, 110, 3), ((10, 100, 3))),
+                 ((10, 110), ((10, 100))),
+                 ((110,), (100,))
+                 ]:
+             self.assertEqual(list(islice(xrange(100), *args)), range(*tgtargs))
+ 
+ 
+         self.assertRaises(TypeError, islice, xrange(10))
+         self.assertRaises(TypeError, islice, xrange(10), 1, 2, 3, 4)
+         self.assertRaises(ValueError, islice, xrange(10), -5, 10, 1)
+         self.assertRaises(ValueError, islice, xrange(10), 1, -5, -1)
+         self.assertRaises(ValueError, islice, xrange(10), 1, 10, -1)
+         self.assertRaises(ValueError, islice, xrange(10), 1, 10, 0)
  
  def test_main():

Index: todo.txt
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/itertools/todo.txt,v
retrieving revision 1.5
retrieving revision 1.6
diff -C2 -d -r1.5 -r1.6
*** todo.txt	27 Jan 2003 05:24:40 -0000	1.5
--- todo.txt	27 Jan 2003 10:03:07 -0000	1.6
***************
*** 1,5 ****
  Add:
!    islice(iterator, start, stop, step)
!    iapply
     takewhile(pred, seqn)
     dropwhile(pred, seqn)
--- 1,4 ----
  Add:
!    iapply(func)  			?? what did this do in SML
     takewhile(pred, seqn)
     dropwhile(pred, seqn)
***************
*** 9,14 ****
     refcounts
  
- 
  Things dropped because they bug me:
     cycle(seqn) requires auxilliary storage (which is surprising
           behavior for iterators).  This is best left for pure python.  
--- 8,16 ----
     refcounts
  
  Things dropped because they bug me:
     cycle(seqn) requires auxilliary storage (which is surprising
           behavior for iterators).  This is best left for pure python.  
+ 
+ Things that just bug me:
+     islice is supposed to looklike i-slice but can be read as is-lice
+