# PEP 276 Simple Iterator for ints (fwd)

James_Althoff at i2.com James_Althoff at i2.com
Wed Dec 12 03:09:08 CET 2001

```Span: The Next Generation ...   :-)

Borrowing from Nick Mathewson and myself, here is another implementation of
intervals that uses relational operators instead of the highly popular /
and // of "span" fame. <wink>

This time the class is called "ints" (as in Nick's example).  Because
relational operators are automatically "and-ed" together when cascaded,
instances of class ints require side effects to maintain state.  This means
that a single instance ("span", for example) won't work.  Instead, it is
necessary to do ints() always.

Another limitation (compared to "span") is that one-sided relational
shortcuts are only partially supported.  Because the magic methods for
relational operators don't have leftside and rightside versions it isn't
possible (unless I'm missing something) to distinguish (in the magic
methods) between "ints() < 5" and "5 > ints()", for example.  So to keep
things simple within the bounds of this limitation, the current
implementation returns (for one-sided shortcuts) [] (an empty list) except
for the common cases of "ints() < n" and "ints() <= n" where n is positive.
In both cases, 0 (inclusive) is the start of the interval.  (Actually, "n >
ints()" and "n >= ints()" do the same thing because of the limitation noted
above).

"Steps" are implemented using a (positive) "step" constructor argument as
in "ints(step=2)", for example.

This example implementation supports non-integer bounds as requested by
interested posters (note to David :-).

Also, this implementation doesn't use xrange and so you can do a closed
interval that includes sys.maxint (by popular demand, not ;-).

Examples and code are included below.

(To take the next step and go from "-5 <= ints() <= 5" to "-5 <= ... <= 5"
we would need syntax changes that make "..." an alternate spelling for
"ints()" when used in a relational expression.  I have no idea if such a
thing is feasible.)

Happy interval-ing,

Jim

===================================================

C:\>python
Python 2.2b1 (#25, Oct 19 2001, 11:44:52) [MSC 32 bit (Intel)] on win32
>>>

... after imports, etc.

>>>
>>> for i in -5 <= ints() <= 5:
...     print i,
...
-5 -4 -3 -2 -1 0 1 2 3 4 5
>>>
>>> for i in -5 < ints() < 5:
...     print i,
...
-4 -3 -2 -1 0 1 2 3 4
>>>
>>> for i in 5 >= ints() >= -5:
...     print i,
...
5 4 3 2 1 0 -1 -2 -3 -4 -5
>>>
>>> for i in ints() < 5:
...     print i,
...
0 1 2 3 4
>>>
>>> for i in -5 <= ints(step=2) <= 5:
...     print i,
...
-5 -3 -1 1 3 5
>>>
>>> for i in -4.5 < ints() < 4.5:
...     print i,
...
-4 -3 -2 -1 0 1 2 3 4
>>>
>>> for i in 4.0 >= ints(step=2) >= -4.0:
...     print i,
...
4 2 0 -2 -4
>>>
>>> for i in ints() < 4.5:
...     print i,
...
0 1 2 3 4
>>>
>>> import sys
>>>
>>> for i in sys.maxint - 2 <= ints() <= sys.maxint:
...     print i,
...
2147483645 2147483646 2147483647
>>>

... test examples

>>> runtests()

passed: 1  -5 <= ints() <=  5    [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5]
passed: 1  -5 <= ints() <   5    [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4]
passed: 1  -5 <  ints() <   5    [-4, -3, -2, -1, 0, 1, 2, 3, 4]
passed: 1  -5 <  ints() <=  5    [-4, -3, -2, -1, 0, 1, 2, 3, 4, 5]
passed: 1   5 >= ints() >= -5    [5, 4, 3, 2, 1, 0, -1, -2, -3, -4, -5]
passed: 1   5 >= ints() >  -5    [5, 4, 3, 2, 1, 0, -1, -2, -3, -4]
passed: 1   5 >  ints() >  -5    [4, 3, 2, 1, 0, -1, -2, -3, -4]
passed: 1   5 >  ints() >= -5    [4, 3, 2, 1, 0, -1, -2, -3, -4, -5]
passed: 1  -5 <= ints(step=2) <=  5    [-5, -3, -1, 1, 3, 5]
passed: 1  -5 <= ints(step=2) <   5    [-5, -3, -1, 1, 3]
passed: 1  -5 <  ints(step=2) <   5    [-4, -2, 0, 2, 4]
passed: 1  -5 <  ints(step=2) <=  5    [-4, -2, 0, 2, 4]
passed: 1   5 >= ints(step=2) >= -5    [5, 3, 1, -1, -3, -5]
passed: 1   5 >= ints(step=2) >  -5    [5, 3, 1, -1, -3]
passed: 1   5 >  ints(step=2) >  -5    [4, 2, 0, -2, -4]
passed: 1   5 >  ints(step=2) >= -5    [4, 2, 0, -2, -4]
passed: 1  ints() <= 5    [0, 1, 2, 3, 4, 5]
passed: 1  ints() <  5    [0, 1, 2, 3, 4]
passed: 1  5 >= ints()    [0, 1, 2, 3, 4, 5]
passed: 1  5 >  ints()    [0, 1, 2, 3, 4]
passed: 1  ints() >= 5    []
passed: 1  ints() >  5    []
passed: 1  5 <= ints()    []
passed: 1  5 <  ints()    []
passed: 1  ints() <= -5    []
passed: 1  ints() <  -5    []
passed: 1  ints() >= -5    []
passed: 1  ints() >  -5    []
passed: 1  -5 <= ints()    []
passed: 1  -5 <  ints()    []
passed: 1  -5 >= ints()    []
passed: 1  -5 >  ints()    []
passed: 1  sys.maxint-1 < ints() <= sys.maxint    [2147483647]
passed: 1  1.5 <= ints() <= 4.5    [2, 3, 4]
passed: 1  1.5 <= ints() <  4.5    [2, 3, 4]
passed: 1  1.5 <  ints() <  4.5    [2, 3, 4]
passed: 1  1.5 <  ints() <= 4.5    [2, 3, 4]
passed: 1  1.0 <= ints() <= 4.0    [1, 2, 3, 4]
passed: 1  1.0 <= ints() <  4.0    [1, 2, 3]
passed: 1  1.0 <  ints() <  4.0    [2, 3]
passed: 1  1.0 <  ints() <= 4.0    [2, 3, 4]
passed: 1  -4.5 <= ints() <= -1.5    [-4, -3, -2]
passed: 1  -4.5 <= ints() <  -1.5    [-4, -3, -2]
passed: 1  -4.5 <  ints() <  -1.5    [-4, -3, -2]
passed: 1  -4.5 <  ints() <= -1.5    [-4, -3, -2]
passed: 1  -4.0 <= ints() <= -1.0    [-4, -3, -2, -1]
passed: 1  -4.0 <= ints() <  -1.0    [-4, -3, -2]
passed: 1  -4.0 <  ints() <  -1.0    [-3, -2]
passed: 1  -4.0 <  ints() <= -1.0    [-3, -2, -1]
passed: 1  ints() <= 4.5    [0, 1, 2, 3, 4]
passed: 1  ints() <  4.5    [0, 1, 2, 3, 4]
passed: 1  ints() <= 4.0    [0, 1, 2, 3, 4]
passed: 1  ints() <  4.0    [0, 1, 2, 3]
passed: 1  ints() > 4.5    []
passed: 1  4.5 < ints()    []

=================================================

# Example implementation for Python 2.2:

from __future__ import generators
from math import floor,ceil

class ints:

import operator

testDict = {  # i is in interval if this test is true
(1,0):operator.__lt__,  # up == 1, upperClosed == 0
(1,1):operator.__le__,  # up == 1, upperClosed == 1
(0,0):operator.__gt__,  # up == 0, lowerClosed == 0
(0,1):operator.__ge__}  # up == 0, lowerClosed == 1

def __init__(self,step=None):
if step is not None and step <= 0:
raise ValueError
self.step = step  # None or integer > 0
self.lower = None  # lower bound of interval
self.lowerClosed = None  # 1: yes, 0: no
self.upper = None  # upper bound of interval
self.upperClosed = None  # 1: yes, 0: no
self.up = None  # 1: increasing interval, 0: decreasing interval

def __iter__(self):
'''return an iterator that generates integers in the interval.'''
if self.upper is None:
return  # interval is [] if no upper bound specified
start,stop,delta,test = self.getBounds()  # delta is positive or
negative
i = start
while 1:
if not test(i,stop):
break
yield i
i += delta

def getBounds(self):
lower = self.lower
upper = self.upper
lowerClosed = self.lowerClosed
upperClosed = self.upperClosed
up = self.up
delta = self.step
# fill in missing data
if delta is None:
delta = 1
if lower is None:
lower = 0
up = 1
if lowerClosed is None:
lowerClosed = 1
if up is None:
up = 1
if up:
start = lower
floorStart = floor(start)
if not (floorStart == start and lowerClosed):
start = floorStart + 1
stop = upper
test = self.testDict[up,upperClosed]
else:
start = upper
ceilStart = ceil(start)
if not (ceilStart == start and upperClosed):
start = ceilStart - 1
stop = lower
delta = -delta
test = self.testDict[up,lowerClosed]
start = int(start)
return start,stop,delta,test

def __lt__(self,n):
self.upper = n
self.upperClosed = 0
if self.up is None:
self.up = 0  # on first encounter, assume decreasing
return self

def __le__(self,n):
self.upper = n
self.upperClosed = 1
if self.up is None:
self.up = 0  # on first encounter, assume decreasing
return self

def __gt__(self,n):
self.lower = n
self.lowerClosed = 0
if self.up is None:
self.up = 1  # on first encounter, assume increasing
return self

def __ge__(self,n):
self.lower = n
self.lowerClosed = 1
if self.up is None:
self.up = 1  # on first encounter, assume increasing
return self

def __nonzero__(self):
return 1  # force evaluation of both sides of two-way relationals

#\$ Testing

def test(testItem):
import sys
interval = eval(testItem[0],globals(),{'sys':sys})
result = list(interval) == testItem[1]
print 'passed:', result, '', testItem[0], '  ', list(interval)
if not result:
print interval
print list(interval)
print testItem[1]

def runtests():
import sys
testList = [
('-5 <= ints() <=  5', range(-5,6)),
('-5 <= ints() <   5', range(-5,5)),
('-5 <  ints() <   5', range(-4,5)),
('-5 <  ints() <=  5', range(-4,6)),
(' 5 >= ints() >= -5', range(5,-6,-1)),
(' 5 >= ints() >  -5', range(5,-5,-1)),
(' 5 >  ints() >  -5', range(4,-5,-1)),
(' 5 >  ints() >= -5', range(4,-6,-1)),
('-5 <= ints(step=2) <=  5', range(-5,6,2)),
('-5 <= ints(step=2) <   5', range(-5,5,2)),
('-5 <  ints(step=2) <   5', range(-4,5,2)),
('-5 <  ints(step=2) <=  5', range(-4,6,2)),
(' 5 >= ints(step=2) >= -5', range(5,-6,-2)),
(' 5 >= ints(step=2) >  -5', range(5,-5,-2)),
(' 5 >  ints(step=2) >  -5', range(4,-5,-2)),
(' 5 >  ints(step=2) >= -5', range(4,-6,-2)),
('ints() <= 5',  range(0,6)),
('ints() <  5',  range(0,5)),
('5 >= ints()',  range(0,6)),
('5 >  ints()',  range(0,5)),
('ints() >= 5', []),
('ints() >  5', []),
('5 <= ints()',  []),
('5 <  ints()',  []),
('ints() <= -5', []),
('ints() <  -5', []),
('ints() >= -5', []),
('ints() >  -5', []),
('-5 <= ints()', []),
('-5 <  ints()', []),
('-5 >= ints()', []),
('-5 >  ints()', []),
('sys.maxint-1 < ints() <= sys.maxint', [sys.maxint]),
('1.5 <= ints() <= 4.5', [2,3,4]),
('1.5 <= ints() <  4.5', [2,3,4]),
('1.5 <  ints() <  4.5', [2,3,4]),
('1.5 <  ints() <= 4.5', [2,3,4]),
('1.0 <= ints() <= 4.0', [1,2,3,4]),
('1.0 <= ints() <  4.0', [1,2,3]),
('1.0 <  ints() <  4.0', [2,3]),
('1.0 <  ints() <= 4.0', [2,3,4]),
('-4.5 <= ints() <= -1.5', [-4,-3,-2]),
('-4.5 <= ints() <  -1.5', [-4,-3,-2]),
('-4.5 <  ints() <  -1.5', [-4,-3,-2]),
('-4.5 <  ints() <= -1.5', [-4,-3,-2]),
('-4.0 <= ints() <= -1.0', [-4,-3,-2,-1]),
('-4.0 <= ints() <  -1.0', [-4,-3,-2]),
('-4.0 <  ints() <  -1.0', [-3,-2]),
('-4.0 <  ints() <= -1.0', [-3,-2,-1]),
('ints() <= 4.5', [0,1,2,3,4]),
('ints() <  4.5', [0,1,2,3,4]),
('ints() <= 4.0', [0,1,2,3,4]),
('ints() <  4.0', [0,1,2,3]),
('ints() > 4.5', []),
('4.5 < ints()', []),
]
print
for testItem in testList:
test(testItem)
print

```