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 n = (self.end - self.start) / self.step 77 last = self.start + (n * self.step) 78 if last == self.end: 79 return n 80 else: 81 return n + 1 82 83 def __iter__(self): 84 85 "Return an iterator, currently self." 86 87 return xrangeiterator(self.start, self.step, self.__len__()) 88 89 class xrangeiterator: 90 91 "An iterator over an xrange." 92 93 def __init__(self, start, step, count): 94 95 "Initialise the iterator with the given 'obj'." 96 97 self.current = start 98 self.step = step 99 self.count = count 100 101 def next(self): 102 103 "Return the next item or raise a StopIteration exception." 104 105 if not self.count: 106 raise StopIteration 107 108 current = self.current 109 self.current = self.current.__add__(self.step) 110 self.count = self.count.__sub__(1) 111 return current 112 113 def range(start_or_end, end=None, step=1): 114 115 "Implementation of range." 116 117 return list(xrange(start_or_end, end, step)) 118 119 def _max(x, y): 120 121 "Return the maximum of 'x' and 'y'." 122 123 if x >= y: 124 return x 125 else: 126 return y 127 128 def _min(x, y): 129 130 "Return the minimum of 'x' and 'y'." 131 132 if x <= y: 133 return x 134 else: 135 return y 136 137 # vim: tabstop=4 expandtab shiftwidth=4