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) 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): 81 82 "Return a slice starting from 'start', with the optional 'end'." 83 84 length = self.__len__() 85 86 # Handle a null start as the first position, otherwise normalising any 87 # start index. 88 89 if start is None: 90 start = 0 91 else: 92 start = _get_absolute_index(start, length) 93 94 # Handle a null end as the first position after the end of the sequence, 95 # otherwise normalising any end index. 96 97 if end is None: 98 end = length 99 else: 100 end = _get_absolute_index(end, length) 101 102 result = [] 103 104 while start < end: 105 result.append(self.__get_single_item__(start)) 106 start += 1 107 108 return result 109 110 class sequence(itemaccess): 111 112 "A common base class for sequence types." 113 114 def _str(self, opening, closing): 115 116 "Serialise this object with the given 'opening' and 'closing' strings." 117 118 b = buffer() 119 i = 0 120 l = self.__len__() 121 first = True 122 123 b.append(opening) 124 while i < l: 125 if first: 126 first = False 127 else: 128 b.append(", ") 129 b.append(repr(self.__get_single_item__(i))) 130 i += 1 131 b.append(closing) 132 133 return str(b) 134 135 def __contains__(self, value): 136 137 "Return whether the list contains 'value'." 138 139 # Perform a linear search of the sequence contents. 140 141 for v in self: 142 143 # Return True if the current value is equal to the specified one. 144 # Note that this is not an identity test, but an equality test. 145 146 if v == value: 147 return True 148 149 return False 150 151 def index(self, value): 152 153 "Return the index of 'value' or raise ValueError." 154 155 i = 0 156 l = len(self) 157 while i < l: 158 if self[i] == value: 159 return i 160 i += 1 161 162 raise ValueError(value) 163 164 def __eq__(self, other): 165 166 "Return whether this sequence is equal to 'other'." 167 168 # Sequences must have equal lengths to be equal. 169 170 n = self.__len__() 171 if len(other) != n: 172 return False 173 174 i = 0 175 while i < n: 176 if self.__getitem__(i) != other.__getitem__(i): 177 return False 178 i += 1 179 180 return True 181 182 def __ne__(self, other): 183 184 "Return whether this sequence is not equal to 'other'." 185 186 return not self.__eq__(other) 187 188 def _get_absolute_index(index, length): 189 190 """ 191 Return the absolute index for 'index' given a collection having the 192 specified 'length'. 193 """ 194 195 if index < 0: 196 return length + index 197 else: 198 return index 199 200 def _max(x, y): 201 202 "Return the maximum of 'x' and 'y'." 203 204 if x >= y: 205 return x 206 else: 207 return y 208 209 def _min(x, y): 210 211 "Return the minimum of 'x' and 'y'." 212 213 if x <= y: 214 return x 215 else: 216 return y 217 218 # vim: tabstop=4 expandtab shiftwidth=4