1 #!/usr/bin/env python 2 3 """ 4 Sequence operations. 5 6 Copyright (C) 2015, 2016 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 from native import _isinstance 23 24 class itemaccess: 25 26 "An abstract class providing item access." 27 28 def _check_index(self, index): 29 30 """ 31 Check the given absolute 'index', raising an IndexError if out of 32 bounds. 33 """ 34 35 if index < 0 or index >= len(self): 36 raise IndexError(index) 37 38 def __getitem__(self, index): 39 40 "Return the item or slice specified by 'index'." 41 42 # Normalise any integer indexes, converting negative indexes to positive 43 # ones. 44 45 if _isinstance(index, int): 46 index = _get_absolute_index(index, self.__len__()) 47 return self.__get_single_item__(index) 48 49 # Handle slices separately. 50 51 elif _isinstance(index, slice): 52 return self.__getslice__(index.start, index.end, index.step) 53 54 # No other kinds of objects are supported as indexes. 55 56 else: 57 raise TypeError() 58 59 def __setitem__(self, index, value): 60 61 "Set at 'index' the given 'value'." 62 63 # Normalise any integer indexes, converting negative indexes to positive 64 # ones. 65 66 if _isinstance(index, int): 67 index = _get_absolute_index(index, self.__len__()) 68 return self.__set_single_item__(index, value) 69 70 # Handle slices separately. 71 72 elif _isinstance(index, slice): 73 return self.__setslice__(index.start, index.end, value) 74 75 # No other kinds of objects are supported as indexes. 76 77 else: 78 raise TypeError() 79 80 def __getslice__(self, start, end=None, step=1): 81 82 """ 83 Return a slice of the sequence starting from the 'start' index, ending 84 before the optional 'end' (or at the end of the sequence), and providing 85 items at the frequency given by 'step' (with a default step of 1). 86 """ 87 88 if step == 0: 89 raise ValueError(step) 90 91 length = self.__len__() 92 93 # Handle a null start as the first position, otherwise normalising any 94 # start index. 95 96 if start is None: 97 start = 0 98 else: 99 start = _get_absolute_index(start, length) 100 101 # Handle a null end as the first position after the end of the sequence, 102 # otherwise normalising any end index. 103 104 if end is None: 105 end = length 106 else: 107 end = _get_absolute_index(end, length) 108 109 result = [] 110 111 while step > 0 and start < end or step < 0 and start > end: 112 result.append(self.__get_single_item__(start)) 113 start += step 114 115 return result 116 117 class sequence(itemaccess): 118 119 "A common base class for sequence types." 120 121 def _str(self, opening, closing): 122 123 "Serialise this object with the given 'opening' and 'closing' strings." 124 125 b = buffer() 126 i = 0 127 l = self.__len__() 128 first = True 129 130 b.append(opening) 131 while i < l: 132 if first: 133 first = False 134 else: 135 b.append(", ") 136 b.append(repr(self.__get_single_item__(i))) 137 i += 1 138 b.append(closing) 139 140 return str(b) 141 142 def __contains__(self, value): 143 144 "Return whether the list contains 'value'." 145 146 # Perform a linear search of the sequence contents. 147 148 for v in self: 149 150 # Return True if the current value is equal to the specified one. 151 # Note that this is not an identity test, but an equality test. 152 153 if v == value: 154 return True 155 156 return False 157 158 def index(self, value): 159 160 "Return the index of 'value' or raise ValueError." 161 162 i = 0 163 l = len(self) 164 while i < l: 165 if self[i] == value: 166 return i 167 i += 1 168 169 raise ValueError(value) 170 171 def __eq__(self, other): 172 173 "Return whether this sequence is equal to 'other'." 174 175 # Sequences must have equal lengths to be equal. 176 177 n = self.__len__() 178 if len(other) != n: 179 return False 180 181 i = 0 182 while i < n: 183 if self.__getitem__(i) != other.__getitem__(i): 184 return False 185 i += 1 186 187 return True 188 189 def __ne__(self, other): 190 191 "Return whether this sequence is not equal to 'other'." 192 193 return not self.__eq__(other) 194 195 def _get_absolute_index(index, length): 196 197 """ 198 Return the absolute index for 'index' given a collection having the 199 specified 'length'. 200 """ 201 202 if index < 0: 203 return length + index 204 else: 205 return index 206 207 def _max(x, y): 208 209 "Return the maximum of 'x' and 'y'." 210 211 if x >= y: 212 return x 213 else: 214 return y 215 216 def _min(x, y): 217 218 "Return the minimum of 'x' and 'y'." 219 220 if x <= y: 221 return x 222 else: 223 return y 224 225 # vim: tabstop=4 expandtab shiftwidth=4