Lichen

lib/__builtins__/span.py

907:018b653418f2
2019-06-08 Paul Boddie Added an optional iterator attribute to StopIteration.
     1 #!/usr/bin/env python     2      3 """     4 Span-related objects.     5      6 Copyright (C) 2015, 2016, 2017, 2018, 2019 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         return "%s.%s(%r, %r, %r)" % (self.__parent__.__name__, self.__name__,    49                                       self.start,  self.end, self.step)    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 'start', 'step' and 'count'."    96     97         self.current = start    98     99     def next(self):   100    101         "Return the next item or raise a StopIteration exception."   102    103         if not self.count:   104             raise StopIteration, self   105    106         current = self.current   107         self.current = self.current.__add__(self.step)   108         self.count = self.count.__sub__(1)   109         return current   110    111 def range(start_or_end, end=None, step=1):   112    113     "Implementation of range."   114    115     return list(xrange(start_or_end, end, step))   116    117 def _max(x, y):   118    119     "Return the maximum of 'x' and 'y'."   120    121     if x >= y:   122         return x   123     else:   124         return y   125    126 def _min(x, y):   127    128     "Return the minimum of 'x' and 'y'."   129    130     if x <= y:   131         return x   132     else:   133         return y   134    135 # vim: tabstop=4 expandtab shiftwidth=4