1 #!/usr/bin/env python 2 3 """ 4 Span-related objects. 5 6 Copyright (C) 2015, 2016, 2017 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 class slice: 23 24 "Implementation of slice." 25 26 NO_END = object() 27 28 def __init__(self, start_or_end=None, end=NO_END, step=1): 29 30 "Initialise the slice with the given 'start_or_end', 'end' and 'step'." 31 32 if end is slice.NO_END: 33 self.start = 0 34 self.end = start_or_end 35 else: 36 self.start = start_or_end 37 self.end = end 38 39 if step == 0: 40 raise ValueError(self.step) 41 42 self.step = step 43 44 def __str__(self): 45 46 "Return a string representation." 47 48 b = buffer([self.__parent__.__name__, ".", self.__name__, "(", self.start, ", ", self.end, ", ", self.step, ")"]) 49 return str(b) 50 51 __repr__ = __str__ 52 53 class xrange(slice): 54 55 "Implementation of xrange." 56 57 def __init__(self, start_or_end, end=slice.NO_END, step=1): 58 59 "Initialise the xrange with the given 'start_or_end', 'end' and 'step'." 60 61 get_using(slice.__init__, self)(start_or_end, end, step) 62 63 # Constrain the end according to the start and step. 64 65 if step > 0: 66 self.end = _max(self.start, self.end) 67 elif step < 0: 68 self.end = _min(self.start, self.end) 69 else: 70 raise ValueError(self.step) 71 72 def __len__(self): 73 74 "Return the length of the range." 75 76 return (self.end - self.start) / self.step 77 78 def __iter__(self): 79 80 "Return an iterator, currently self." 81 82 return xrangeiterator(self) 83 84 class xrangeiterator: 85 86 "An iterator over an xrange." 87 88 def __init__(self, obj): 89 90 "Initialise the iterator with the given 'obj'." 91 92 self.start = obj.start 93 self.end = obj.end 94 self.step = obj.step 95 self.current = obj.start 96 97 def next(self): 98 99 "Return the next item or raise a StopIteration exception." 100 101 if self.step < 0 and self.current <= self.end or self.step > 0 and self.current >= self.end: 102 raise StopIteration 103 104 current = self.current 105 self.current += self.step 106 return current 107 108 def range(start_or_end, end=None, step=1): 109 110 "Implementation of range." 111 112 return list(xrange(start_or_end, end, step)) 113 114 def _max(x, y): 115 116 "Return the maximum of 'x' and 'y'." 117 118 if x >= y: 119 return x 120 else: 121 return y 122 123 def _min(x, y): 124 125 "Return the minimum of 'x' and 'y'." 126 127 if x <= y: 128 return x 129 else: 130 return y 131 132 # vim: tabstop=4 expandtab shiftwidth=4