Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pyrsistent/_pdeque.py: 29%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from collections.abc import Sequence, Hashable
2from itertools import islice, chain
3from numbers import Integral
4from typing import TypeVar, Generic
5from pyrsistent._plist import plist
7T_co = TypeVar('T_co', covariant=True)
10class PDeque(Generic[T_co]):
11 """
12 Persistent double ended queue (deque). Allows quick appends and pops in both ends. Implemented
13 using two persistent lists.
15 A maximum length can be specified to create a bounded queue.
17 Fully supports the Sequence and Hashable protocols including indexing and slicing but
18 if you need fast random access go for the PVector instead.
20 Do not instantiate directly, instead use the factory functions :py:func:`dq` or :py:func:`pdeque` to
21 create an instance.
23 Some examples:
25 >>> x = pdeque([1, 2, 3])
26 >>> x.left
27 1
28 >>> x.right
29 3
30 >>> x[0] == x.left
31 True
32 >>> x[-1] == x.right
33 True
34 >>> x.pop()
35 pdeque([1, 2])
36 >>> x.pop() == x[:-1]
37 True
38 >>> x.popleft()
39 pdeque([2, 3])
40 >>> x.append(4)
41 pdeque([1, 2, 3, 4])
42 >>> x.appendleft(4)
43 pdeque([4, 1, 2, 3])
45 >>> y = pdeque([1, 2, 3], maxlen=3)
46 >>> y.append(4)
47 pdeque([2, 3, 4], maxlen=3)
48 >>> y.appendleft(4)
49 pdeque([4, 1, 2], maxlen=3)
50 """
51 __slots__ = ('_left_list', '_right_list', '_length', '_maxlen', '__weakref__')
53 def __new__(cls, left_list, right_list, length, maxlen=None):
54 instance = super(PDeque, cls).__new__(cls)
55 instance._left_list = left_list
56 instance._right_list = right_list
57 instance._length = length
59 if maxlen is not None:
60 if not isinstance(maxlen, Integral):
61 raise TypeError('An integer is required as maxlen')
63 if maxlen < 0:
64 raise ValueError("maxlen must be non-negative")
66 instance._maxlen = maxlen
67 return instance
69 @property
70 def right(self):
71 """
72 Rightmost element in dqueue.
73 """
74 return PDeque._tip_from_lists(self._right_list, self._left_list)
76 @property
77 def left(self):
78 """
79 Leftmost element in dqueue.
80 """
81 return PDeque._tip_from_lists(self._left_list, self._right_list)
83 @staticmethod
84 def _tip_from_lists(primary_list, secondary_list):
85 if primary_list:
86 return primary_list.first
88 if secondary_list:
89 return secondary_list[-1]
91 raise IndexError('No elements in empty deque')
93 def __iter__(self):
94 return chain(self._left_list, self._right_list.reverse())
96 def __repr__(self):
97 return "pdeque({0}{1})".format(list(self),
98 ', maxlen={0}'.format(self._maxlen) if self._maxlen is not None else '')
99 __str__ = __repr__
101 @property
102 def maxlen(self):
103 """
104 Maximum length of the queue.
105 """
106 return self._maxlen
108 def pop(self, count=1):
109 """
110 Return new deque with rightmost element removed. Popping the empty queue
111 will return the empty queue. A optional count can be given to indicate the
112 number of elements to pop. Popping with a negative index is the same as
113 popleft. Executes in amortized O(k) where k is the number of elements to pop.
115 >>> pdeque([1, 2]).pop()
116 pdeque([1])
117 >>> pdeque([1, 2]).pop(2)
118 pdeque([])
119 >>> pdeque([1, 2]).pop(-1)
120 pdeque([2])
121 """
122 if count < 0:
123 return self.popleft(-count)
125 new_right_list, new_left_list = PDeque._pop_lists(self._right_list, self._left_list, count)
126 return PDeque(new_left_list, new_right_list, max(self._length - count, 0), self._maxlen)
128 def popleft(self, count=1):
129 """
130 Return new deque with leftmost element removed. Otherwise functionally
131 equivalent to pop().
133 >>> pdeque([1, 2]).popleft()
134 pdeque([2])
135 """
136 if count < 0:
137 return self.pop(-count)
139 new_left_list, new_right_list = PDeque._pop_lists(self._left_list, self._right_list, count)
140 return PDeque(new_left_list, new_right_list, max(self._length - count, 0), self._maxlen)
142 @staticmethod
143 def _pop_lists(primary_list, secondary_list, count):
144 new_primary_list = primary_list
145 new_secondary_list = secondary_list
147 while count > 0 and (new_primary_list or new_secondary_list):
148 count -= 1
149 if new_primary_list.rest:
150 new_primary_list = new_primary_list.rest
151 elif new_primary_list:
152 new_primary_list = new_secondary_list.reverse()
153 new_secondary_list = plist()
154 else:
155 new_primary_list = new_secondary_list.reverse().rest
156 new_secondary_list = plist()
158 return new_primary_list, new_secondary_list
160 def _is_empty(self):
161 return not self._left_list and not self._right_list
163 def __lt__(self, other):
164 if not isinstance(other, PDeque):
165 return NotImplemented
167 return tuple(self) < tuple(other)
169 def __eq__(self, other):
170 if not isinstance(other, PDeque):
171 return NotImplemented
173 if tuple(self) == tuple(other):
174 # Sanity check of the length value since it is redundant (there for performance)
175 assert len(self) == len(other)
176 return True
178 return False
180 def __hash__(self):
181 return hash(tuple(self))
183 def __len__(self):
184 return self._length
186 def append(self, elem):
187 """
188 Return new deque with elem as the rightmost element.
190 >>> pdeque([1, 2]).append(3)
191 pdeque([1, 2, 3])
192 """
193 new_left_list, new_right_list, new_length = self._append(self._left_list, self._right_list, elem)
194 return PDeque(new_left_list, new_right_list, new_length, self._maxlen)
196 def appendleft(self, elem):
197 """
198 Return new deque with elem as the leftmost element.
200 >>> pdeque([1, 2]).appendleft(3)
201 pdeque([3, 1, 2])
202 """
203 new_right_list, new_left_list, new_length = self._append(self._right_list, self._left_list, elem)
204 return PDeque(new_left_list, new_right_list, new_length, self._maxlen)
206 def _append(self, primary_list, secondary_list, elem):
207 if self._maxlen is not None and self._length == self._maxlen:
208 if self._maxlen == 0:
209 return primary_list, secondary_list, 0
210 new_primary_list, new_secondary_list = PDeque._pop_lists(primary_list, secondary_list, 1)
211 return new_primary_list, new_secondary_list.cons(elem), self._length
213 return primary_list, secondary_list.cons(elem), self._length + 1
215 @staticmethod
216 def _extend_list(the_list, iterable):
217 count = 0
218 for elem in iterable:
219 the_list = the_list.cons(elem)
220 count += 1
222 return the_list, count
224 def _extend(self, primary_list, secondary_list, iterable):
225 new_primary_list, extend_count = PDeque._extend_list(primary_list, iterable)
226 new_secondary_list = secondary_list
227 current_len = self._length + extend_count
228 if self._maxlen is not None and current_len > self._maxlen:
229 pop_len = current_len - self._maxlen
230 new_secondary_list, new_primary_list = PDeque._pop_lists(new_secondary_list, new_primary_list, pop_len)
231 extend_count -= pop_len
233 return new_primary_list, new_secondary_list, extend_count
235 def extend(self, iterable):
236 """
237 Return new deque with all elements of iterable appended to the right.
239 >>> pdeque([1, 2]).extend([3, 4])
240 pdeque([1, 2, 3, 4])
241 """
242 new_right_list, new_left_list, extend_count = self._extend(self._right_list, self._left_list, iterable)
243 return PDeque(new_left_list, new_right_list, self._length + extend_count, self._maxlen)
245 def extendleft(self, iterable):
246 """
247 Return new deque with all elements of iterable appended to the left.
249 NB! The elements will be inserted in reverse order compared to the order in the iterable.
251 >>> pdeque([1, 2]).extendleft([3, 4])
252 pdeque([4, 3, 1, 2])
253 """
254 new_left_list, new_right_list, extend_count = self._extend(self._left_list, self._right_list, iterable)
255 return PDeque(new_left_list, new_right_list, self._length + extend_count, self._maxlen)
257 def count(self, elem):
258 """
259 Return the number of elements equal to elem present in the queue
261 >>> pdeque([1, 2, 1]).count(1)
262 2
263 """
264 return self._left_list.count(elem) + self._right_list.count(elem)
266 def remove(self, elem):
267 """
268 Return new deque with first element from left equal to elem removed. If no such element is found
269 a ValueError is raised.
271 >>> pdeque([2, 1, 2]).remove(2)
272 pdeque([1, 2])
273 """
274 try:
275 return PDeque(self._left_list.remove(elem), self._right_list, self._length - 1)
276 except ValueError:
277 # Value not found in left list, try the right list
278 try:
279 # This is severely inefficient with a double reverse, should perhaps implement a remove_last()?
280 return PDeque(self._left_list,
281 self._right_list.reverse().remove(elem).reverse(), self._length - 1)
282 except ValueError as e:
283 raise ValueError('{0} not found in PDeque'.format(elem)) from e
285 def reverse(self):
286 """
287 Return reversed deque.
289 >>> pdeque([1, 2, 3]).reverse()
290 pdeque([3, 2, 1])
292 Also supports the standard python reverse function.
294 >>> reversed(pdeque([1, 2, 3]))
295 pdeque([3, 2, 1])
296 """
297 return PDeque(self._right_list, self._left_list, self._length)
298 __reversed__ = reverse
300 def rotate(self, steps):
301 """
302 Return deque with elements rotated steps steps.
304 >>> x = pdeque([1, 2, 3])
305 >>> x.rotate(1)
306 pdeque([3, 1, 2])
307 >>> x.rotate(-2)
308 pdeque([3, 1, 2])
309 """
310 popped_deque = self.pop(steps)
311 if steps >= 0:
312 return popped_deque.extendleft(islice(self.reverse(), steps))
314 return popped_deque.extend(islice(self, -steps))
316 def __reduce__(self):
317 # Pickling support
318 return pdeque, (list(self), self._maxlen)
320 def __getitem__(self, index):
321 if isinstance(index, slice):
322 if index.step is not None and index.step != 1:
323 # Too difficult, no structural sharing possible
324 return pdeque(tuple(self)[index], maxlen=self._maxlen)
326 result = self
327 if index.start is not None:
328 result = result.popleft(index.start % self._length)
329 if index.stop is not None:
330 result = result.pop(self._length - (index.stop % self._length))
332 return result
334 if not isinstance(index, Integral):
335 raise TypeError("'%s' object cannot be interpreted as an index" % type(index).__name__)
337 if index >= 0:
338 return self.popleft(index).left
340 shifted = len(self) + index
341 if shifted < 0:
342 raise IndexError(
343 "pdeque index {0} out of range {1}".format(index, len(self)),
344 )
345 return self.popleft(shifted).left
347 index = Sequence.index
349Sequence.register(PDeque)
350Hashable.register(PDeque)
353def pdeque(iterable=(), maxlen=None):
354 """
355 Return deque containing the elements of iterable. If maxlen is specified then
356 len(iterable) - maxlen elements are discarded from the left to if len(iterable) > maxlen.
358 >>> pdeque([1, 2, 3])
359 pdeque([1, 2, 3])
360 >>> pdeque([1, 2, 3, 4], maxlen=2)
361 pdeque([3, 4], maxlen=2)
362 """
363 t = tuple(iterable)
364 if maxlen is not None:
365 t = t[-maxlen:]
366 length = len(t)
367 pivot = int(length / 2)
368 left = plist(t[:pivot])
369 right = plist(t[pivot:], reverse=True)
370 return PDeque(left, right, length, maxlen)
372def dq(*elements):
373 """
374 Return deque containing all arguments.
376 >>> dq(1, 2, 3)
377 pdeque([1, 2, 3])
378 """
379 return pdeque(elements)