grscheller.datastructures.queues

Queue based datastructures.

  • stateful queue data structures with amortized O(1) pushes and pops each end
  • obtaining length (number of elements) of a queue is an O(1) operation
  • implemented in a "has-a" relationship with a Python list based circular array
  • these data structures will resize themselves as needed
  1# Copyright 2023-2024 Geoffrey R. Scheller
  2#
  3# Licensed under the Apache License, Version 2.0 (the "License");
  4# you may not use this file except in compliance with the License.
  5# You may obtain a copy of the License at
  6#
  7#     http://www.apache.org/licenses/LICENSE-2.0
  8#
  9# Unless required by applicable law or agreed to in writing, software
 10# distributed under the License is distributed on an "AS IS" BASIS,
 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12# See the License for the specific language governing permissions and
 13# limitations under the License.
 14
 15"""
 16### Queue based datastructures.
 17
 18* stateful queue data structures with amortized O(1) pushes and pops each end
 19* obtaining length (number of elements) of a queue is an O(1) operation
 20* implemented in a "has-a" relationship with a Python list based circular array
 21* these data structures will resize themselves as needed
 22
 23"""
 24
 25from __future__ import annotations
 26
 27from typing import Callable, Generic, Iterator, Optional, Self, TypeVar
 28from typing import overload, cast
 29from grscheller.circular_array.ca import CA
 30from grscheller.fp.woException import MB
 31
 32__all__ = [ 'DoubleQueue', 'FIFOQueue', 'LIFOQueue', 'QueueBase' ]
 33
 34D = TypeVar('D')
 35S = TypeVar('S')
 36U = TypeVar('U')
 37V = TypeVar('V')
 38L = TypeVar('L')
 39R = TypeVar('R')
 40
 41class QueueBase(Generic[D, S]):
 42    """
 43    #### Base class for queues
 44
 45    * primarily for DRY implementation inheritance
 46    * each queue object "has-a" (contains) a circular array to store its data
 47    * len() returns the current number of elements in the queue
 48    * in a boolean context, returns true if not empty
 49
 50    """
 51    __slots__ = '_ca', '_sentinel'
 52
 53    def __init__(self, *ds: D, s: S):
 54        self._ca = CA(*ds)
 55        self._sentinel = s
 56
 57    def __repr__(self) -> str:
 58        if len(self) == 0:
 59            return type(self).__name__ + '(s=' + repr(self._sentinel)+ ')'
 60        else:
 61            return type(self).__name__ + '(' + ', '.join(map(repr, self._ca)) + ', s=' + repr(self._sentinel)+ ')'
 62
 63    def __bool__(self) -> bool:
 64        return len(self._ca) > 0
 65
 66    def __len__(self) -> int:
 67        return len(self._ca)
 68
 69    def __eq__(self, other: object) -> bool:
 70        if not isinstance(other, type(self)):
 71            return False
 72        return self._ca == other._ca
 73
 74class FIFOQueue(QueueBase[D, S]):
 75    """
 76    #### FIFO Queue
 77
 78    * stateful First In First Out (FIFO) data structure.
 79    * will resize itself larger as needed
 80    * initial data pushed on in natural FIFO order
 81    """
 82    __slots__ = ()
 83
 84    def __iter__(self) -> Iterator[D]:
 85        return iter(list(self._ca))
 86
 87    def copy(self) -> FIFOQueue[D, S]:
 88        """
 89        ##### FIFOQueue Copy
 90
 91        Return shallow copy of the FIFOQueue.
 92
 93        """
 94        return FIFOQueue(*self._ca, s=self._sentinel)
 95
 96    def __str__(self) -> str:
 97        return "<< " + " < ".join(map(str, self)) + " <<"
 98
 99    def push(self, *ds: D) -> None:
100        """
101        ##### Push Data
102
103        Push data onto FIFOQueue. Like Python list, does not return
104        a reference to itself.
105        """
106        self._ca.pushR(*ds)
107
108    def pop(self) -> D|S:
109        """
110        ##### Pop Data
111
112        Pop data off of FIFOQueue. Return sentinel value if queue is empty.
113        """
114        if self._ca:
115            return self._ca.popL()
116        else:
117            return self._sentinel
118
119    def peak_last_in(self) -> D|S:
120        """
121        ### Peak Last In
122
123        Without consuming it, if it is still in the queue, return last element
124        pushed. If queue empty, return sentinel value.
125        """
126        if self._ca:
127            return self._ca[-1]
128        else:
129            return self._sentinel
130
131    def peak_next_out(self) -> D|S:
132        """
133        ### Peak Next Out
134
135        Without consuming it, if the queue is not empty, return the next item
136        ready to be popped. Otherwise, return sentinel value.
137        """
138        if self._ca:
139            return self._ca[0]
140        else:
141            return self._sentinel
142
143    @overload
144    def fold(self, f: Callable[[L, D], L], initial: Optional[L]) -> L|S:
145        ...
146    @overload
147    def fold(self, f: Callable[[D, D], D]) -> D|S:
148        ...
149    @overload
150    def fold(self, f: Callable[[L, D], L], initial: L) -> L:
151        ...
152    @overload
153    def fold(self, f: Callable[[D, D], D], initial: D) -> D:
154        ...
155
156    def fold(self, f: Callable[[L, D], L], initial: Optional[L]=None) -> L|S:
157        """
158        ##### Fold in FIFO Order
159
160        * reduce with `f` using an optional initial value
161        * folds in natural FIFO Order (oldest to newest)
162        * note that ~S can be the same type as either ~L or ~D
163        * note that when an initial value is not given then ~L = ~D
164        * if iterable empty & no initial value given, return a sentinel value of type ~S
165        * traditional FP type order given for function f
166
167        """
168        if initial is None:
169            if not self:
170                return self._sentinel
171        return self._ca.foldL(f, initial=initial)
172
173    def map(self, f: Callable[[D], U]) -> FIFOQueue[U, S]:
174        """
175        ##### Map FIFOQueue
176
177        Map the function `f` over the FIFOQueue, oldest to newest. Retain
178        original order.
179
180        """
181        return FIFOQueue(*map(f, self._ca), s=self._sentinel)
182
183class LIFOQueue(QueueBase[D, S]):
184    """
185    #### LIFO Queue
186
187    * Last In First Out (LIFO) stateful queue data structure.
188    * will resize itself larger as needed
189    * initial data pushed on in natural LIFO order
190
191    """
192    __slots__ = ()
193
194    def __iter__(self) -> Iterator[D]:
195        return reversed(list(self._ca))
196
197    def copy(self) -> LIFOQueue[D, S]:
198        """
199        ##### LIFOQueue Copy
200
201        Return shallow copy of the LIFOQueue.
202        """
203        return LIFOQueue(*reversed(self._ca), s=self._sentinel)
204
205    def __str__(self) -> str:
206        return "|| " + " > ".join(map(str, self)) + " ><"
207
208    def push(self, *ds: D) -> None:
209        """
210        ##### Push Data
211
212        Push data on LIFOQueue. Like Python list, does not return
213        a reference to itself.
214        """
215        self._ca.pushR(*ds)
216
217    def pop(self) -> D|S:
218        """
219        ##### Pop LIFO Queue
220
221        Pop data off of LIFOQueue. Return sentinel value if queue is empty.
222        """
223        if self._ca:
224            return self._ca.popR()
225        else:
226            return self._sentinel
227
228    def peak(self) -> D|S:
229        """
230        ##### Peak Next Out
231
232        Without consuming it, if the queue is not empty, return the next item
233        ready to be popped. Otherwise, return sentinel value.
234
235        """
236        if self._ca:
237            return self._ca[-1]
238        else:
239            return self._sentinel
240
241    @overload
242    def fold(self, f: Callable[[D, R], R], initial: Optional[R]) -> R|S:
243        ...
244    @overload
245    def fold(self, f: Callable[[D, D], D]) -> D|S:
246        ...
247    @overload
248    def fold(self, f: Callable[[D, R], R], initial: R) -> R:
249        ...
250    @overload
251    def fold(self, f: Callable[[D, D], D], initial: D) -> D:
252        ...
253
254    def fold(self, f: Callable[[D, R], R], initial: Optional[R]=None) -> R|S:
255        """
256        ##### Fold in LIFO Order
257
258        * reduce with `f` using an optional initial value
259        * folds in natural LIFO Order (newest to oldest)
260        * note that ~S can be the same type as either ~L or ~D
261        * note that when an initial value is not given then ~L = ~D
262        * if iterable empty & no initial value given, return a sentinel value of type ~S
263        * traditional FP type order given for function f
264
265        """
266        if initial is None:
267            if not self:
268                return self._sentinel
269        return self._ca.foldR(f, initial=initial)
270
271    def map(self, f: Callable[[D], U]) -> LIFOQueue[U, S]:
272        """
273        ##### Map LIFOQueue
274
275        Map the function `f` over the LIFOQueue, newest to oldest. Retain
276        original order.
277
278        """
279        return LIFOQueue(*reversed(CA(*map(f, reversed(self._ca)))), s=self._sentinel)
280
281class DoubleQueue(QueueBase[D, S]):
282    """
283    #### Double Ended Queue
284
285    * double ended (DQueue) stateful queue data structure.
286    * will resize itself larger as needed
287    * initial data pushed on in natural LIFO order
288
289    """
290    __slots__ = ()
291
292    def __iter__(self) -> Iterator[D]:
293        return iter(list(self._ca))
294
295    def __reversed__(self) -> Iterator[D]:
296        return reversed(list(self._ca))
297
298    def __str__(self) -> str:
299        return ">< " + " | ".join(map(str, self)) + " ><"
300
301    def copy(self) -> DoubleQueue[D, S]:
302        """
303        ##### DoubleQueue Copy
304
305        Return shallow copy of the DoubleQueue.
306
307        """
308        return DoubleQueue(*self._ca, s=self._sentinel)
309
310    def pushL(self, *ds: D) -> None:
311        """
312        ##### Push Left
313
314        Push data onto front (left side) of queue. Like Python list, does not
315        return a reference to itself.
316
317        """
318        self._ca.pushL(*ds)
319
320    def pushR(self, *ds: D) -> None:
321        """
322        ##### Push Right
323
324        Push data onto rear (right side) of queue. Like Python list, does not
325        return a reference to itself.
326
327        """
328        self._ca.pushR(*ds)
329
330    def popL(self) -> D|S:
331        """
332        ##### Pop Left
333
334        Pop data off front (left side) of DoubleQueue. Return sentinel value if
335        queue is empty.
336
337        """
338        if self._ca:
339            return self._ca.popL()
340        else:
341            return self._sentinel
342
343    def popR(self) -> D|S:
344        """
345        ##### Pop Right
346
347        Pop data off rear (right side) of DoubleQueue. Return sentinel value if
348        queue is empty.
349
350        """
351        if self._ca:
352            return self._ca.popR()
353        else:
354            return self._sentinel
355
356    def peakL(self) -> D|S:
357        """
358        ##### Peak Left
359
360        Return leftmost element of the DoubleQueue if it exists, otherwise
361        return the sentinel value.
362
363        """
364        if self._ca:
365            return self._ca[0]
366        else:
367            return self._sentinel
368
369    def peakR(self) -> D|S:
370        """
371        ##### Peak Right
372
373        Return rightmost element of the DoubleQueue if it exists, otherwise
374        return the sentinel value.
375
376        """
377        if self._ca:
378            return self._ca[-1]
379        else:
380            return self._sentinel
381
382    @overload
383    def foldL(self, f: Callable[[L, D], L], initial: Optional[L]) -> L|S:
384        ...
385    @overload
386    def foldL(self, f: Callable[[D, D], D]) -> D|S:
387        ...
388    @overload
389    def foldL(self, f: Callable[[L, D], L], initial: L) -> L:
390        ...
391    @overload
392    def foldL(self, f: Callable[[D, D], D], initial: D) -> D:
393        ...
394
395    def foldL(self, f: Callable[[L, D], L], initial: Optional[L]=None) -> L|S:
396        """
397        ##### Fold Left
398
399        * reduce left with `f` using an optional initial value
400        * note that ~S can be the same type as either ~L or ~D
401        * note that when an initial value is not given then ~L = ~D
402        * if iterable empty & no initial value given, return a sentinel value of type ~S
403        * traditional FP type order given for function f
404        * folds in natural FIFO Order
405
406        """
407        return self._ca.foldL(f, initial=initial)
408
409    @overload
410    def foldR(self, f: Callable[[D, R], R], initial: Optional[R]) -> R|S:
411        ...
412    @overload
413    def foldR(self, f: Callable[[D, D], D]) -> D|S:
414        ...
415    @overload
416    def foldR(self, f: Callable[[D, R], R], initial: R) -> R:
417        ...
418    @overload
419    def foldR(self, f: Callable[[D, D], D], initial: D) -> D:
420        ...
421
422    def foldR(self, f: Callable[[D, R], R], initial: Optional[R]=None) -> R|S:
423        """
424        ##### Fold Right
425
426        * reduce right with `f` using an optional initial value
427        * note that ~S can be the same type as either ~R or ~D
428        * note that when an initial value is not given then ~R = ~D
429        * if iterable empty & no initial value given, return a sentinel value of type ~S
430        * traditional FP type order given for function f
431        * folds in natural FIFO Order
432
433        """
434        return self._ca.foldR(f, initial=initial)
435
436    def map(self, f: Callable[[D], U]) -> DoubleQueue[U, S]:
437        """
438        ##### Map DoubleQueue
439
440        Map the function `f` over the DoubleQueue, oldest to newest. Retain
441        original order.
442
443        """
444        return DoubleQueue(*map(f, self._ca), s=self._sentinel)
class DoubleQueue(grscheller.datastructures.queues.QueueBase[~D, ~S]):
282class DoubleQueue(QueueBase[D, S]):
283    """
284    #### Double Ended Queue
285
286    * double ended (DQueue) stateful queue data structure.
287    * will resize itself larger as needed
288    * initial data pushed on in natural LIFO order
289
290    """
291    __slots__ = ()
292
293    def __iter__(self) -> Iterator[D]:
294        return iter(list(self._ca))
295
296    def __reversed__(self) -> Iterator[D]:
297        return reversed(list(self._ca))
298
299    def __str__(self) -> str:
300        return ">< " + " | ".join(map(str, self)) + " ><"
301
302    def copy(self) -> DoubleQueue[D, S]:
303        """
304        ##### DoubleQueue Copy
305
306        Return shallow copy of the DoubleQueue.
307
308        """
309        return DoubleQueue(*self._ca, s=self._sentinel)
310
311    def pushL(self, *ds: D) -> None:
312        """
313        ##### Push Left
314
315        Push data onto front (left side) of queue. Like Python list, does not
316        return a reference to itself.
317
318        """
319        self._ca.pushL(*ds)
320
321    def pushR(self, *ds: D) -> None:
322        """
323        ##### Push Right
324
325        Push data onto rear (right side) of queue. Like Python list, does not
326        return a reference to itself.
327
328        """
329        self._ca.pushR(*ds)
330
331    def popL(self) -> D|S:
332        """
333        ##### Pop Left
334
335        Pop data off front (left side) of DoubleQueue. Return sentinel value if
336        queue is empty.
337
338        """
339        if self._ca:
340            return self._ca.popL()
341        else:
342            return self._sentinel
343
344    def popR(self) -> D|S:
345        """
346        ##### Pop Right
347
348        Pop data off rear (right side) of DoubleQueue. Return sentinel value if
349        queue is empty.
350
351        """
352        if self._ca:
353            return self._ca.popR()
354        else:
355            return self._sentinel
356
357    def peakL(self) -> D|S:
358        """
359        ##### Peak Left
360
361        Return leftmost element of the DoubleQueue if it exists, otherwise
362        return the sentinel value.
363
364        """
365        if self._ca:
366            return self._ca[0]
367        else:
368            return self._sentinel
369
370    def peakR(self) -> D|S:
371        """
372        ##### Peak Right
373
374        Return rightmost element of the DoubleQueue if it exists, otherwise
375        return the sentinel value.
376
377        """
378        if self._ca:
379            return self._ca[-1]
380        else:
381            return self._sentinel
382
383    @overload
384    def foldL(self, f: Callable[[L, D], L], initial: Optional[L]) -> L|S:
385        ...
386    @overload
387    def foldL(self, f: Callable[[D, D], D]) -> D|S:
388        ...
389    @overload
390    def foldL(self, f: Callable[[L, D], L], initial: L) -> L:
391        ...
392    @overload
393    def foldL(self, f: Callable[[D, D], D], initial: D) -> D:
394        ...
395
396    def foldL(self, f: Callable[[L, D], L], initial: Optional[L]=None) -> L|S:
397        """
398        ##### Fold Left
399
400        * reduce left with `f` using an optional initial value
401        * note that ~S can be the same type as either ~L or ~D
402        * note that when an initial value is not given then ~L = ~D
403        * if iterable empty & no initial value given, return a sentinel value of type ~S
404        * traditional FP type order given for function f
405        * folds in natural FIFO Order
406
407        """
408        return self._ca.foldL(f, initial=initial)
409
410    @overload
411    def foldR(self, f: Callable[[D, R], R], initial: Optional[R]) -> R|S:
412        ...
413    @overload
414    def foldR(self, f: Callable[[D, D], D]) -> D|S:
415        ...
416    @overload
417    def foldR(self, f: Callable[[D, R], R], initial: R) -> R:
418        ...
419    @overload
420    def foldR(self, f: Callable[[D, D], D], initial: D) -> D:
421        ...
422
423    def foldR(self, f: Callable[[D, R], R], initial: Optional[R]=None) -> R|S:
424        """
425        ##### Fold Right
426
427        * reduce right with `f` using an optional initial value
428        * note that ~S can be the same type as either ~R or ~D
429        * note that when an initial value is not given then ~R = ~D
430        * if iterable empty & no initial value given, return a sentinel value of type ~S
431        * traditional FP type order given for function f
432        * folds in natural FIFO Order
433
434        """
435        return self._ca.foldR(f, initial=initial)
436
437    def map(self, f: Callable[[D], U]) -> DoubleQueue[U, S]:
438        """
439        ##### Map DoubleQueue
440
441        Map the function `f` over the DoubleQueue, oldest to newest. Retain
442        original order.
443
444        """
445        return DoubleQueue(*map(f, self._ca), s=self._sentinel)

Double Ended Queue

  • double ended (DQueue) stateful queue data structure.
  • will resize itself larger as needed
  • initial data pushed on in natural LIFO order
def copy(self) -> DoubleQueue[~D, ~S]:
302    def copy(self) -> DoubleQueue[D, S]:
303        """
304        ##### DoubleQueue Copy
305
306        Return shallow copy of the DoubleQueue.
307
308        """
309        return DoubleQueue(*self._ca, s=self._sentinel)
DoubleQueue Copy

Return shallow copy of the DoubleQueue.

def pushL(self, *ds: ~D) -> None:
311    def pushL(self, *ds: D) -> None:
312        """
313        ##### Push Left
314
315        Push data onto front (left side) of queue. Like Python list, does not
316        return a reference to itself.
317
318        """
319        self._ca.pushL(*ds)
Push Left

Push data onto front (left side) of queue. Like Python list, does not return a reference to itself.

def pushR(self, *ds: ~D) -> None:
321    def pushR(self, *ds: D) -> None:
322        """
323        ##### Push Right
324
325        Push data onto rear (right side) of queue. Like Python list, does not
326        return a reference to itself.
327
328        """
329        self._ca.pushR(*ds)
Push Right

Push data onto rear (right side) of queue. Like Python list, does not return a reference to itself.

def popL(self) -> Union[~D, ~S]:
331    def popL(self) -> D|S:
332        """
333        ##### Pop Left
334
335        Pop data off front (left side) of DoubleQueue. Return sentinel value if
336        queue is empty.
337
338        """
339        if self._ca:
340            return self._ca.popL()
341        else:
342            return self._sentinel
Pop Left

Pop data off front (left side) of DoubleQueue. Return sentinel value if queue is empty.

def popR(self) -> Union[~D, ~S]:
344    def popR(self) -> D|S:
345        """
346        ##### Pop Right
347
348        Pop data off rear (right side) of DoubleQueue. Return sentinel value if
349        queue is empty.
350
351        """
352        if self._ca:
353            return self._ca.popR()
354        else:
355            return self._sentinel
Pop Right

Pop data off rear (right side) of DoubleQueue. Return sentinel value if queue is empty.

def peakL(self) -> Union[~D, ~S]:
357    def peakL(self) -> D|S:
358        """
359        ##### Peak Left
360
361        Return leftmost element of the DoubleQueue if it exists, otherwise
362        return the sentinel value.
363
364        """
365        if self._ca:
366            return self._ca[0]
367        else:
368            return self._sentinel
Peak Left

Return leftmost element of the DoubleQueue if it exists, otherwise return the sentinel value.

def peakR(self) -> Union[~D, ~S]:
370    def peakR(self) -> D|S:
371        """
372        ##### Peak Right
373
374        Return rightmost element of the DoubleQueue if it exists, otherwise
375        return the sentinel value.
376
377        """
378        if self._ca:
379            return self._ca[-1]
380        else:
381            return self._sentinel
Peak Right

Return rightmost element of the DoubleQueue if it exists, otherwise return the sentinel value.

def foldL( self, f: Callable[[~L, ~D], ~L], initial: Optional[~L] = None) -> Union[~L, ~S]:
396    def foldL(self, f: Callable[[L, D], L], initial: Optional[L]=None) -> L|S:
397        """
398        ##### Fold Left
399
400        * reduce left with `f` using an optional initial value
401        * note that ~S can be the same type as either ~L or ~D
402        * note that when an initial value is not given then ~L = ~D
403        * if iterable empty & no initial value given, return a sentinel value of type ~S
404        * traditional FP type order given for function f
405        * folds in natural FIFO Order
406
407        """
408        return self._ca.foldL(f, initial=initial)
Fold Left
  • reduce left with f using an optional initial value
  • note that ~S can be the same type as either ~L or ~D
  • note that when an initial value is not given then ~L = ~D
  • if iterable empty & no initial value given, return a sentinel value of type ~S
  • traditional FP type order given for function f
  • folds in natural FIFO Order
def foldR( self, f: Callable[[~D, ~R], ~R], initial: Optional[~R] = None) -> Union[~R, ~S]:
423    def foldR(self, f: Callable[[D, R], R], initial: Optional[R]=None) -> R|S:
424        """
425        ##### Fold Right
426
427        * reduce right with `f` using an optional initial value
428        * note that ~S can be the same type as either ~R or ~D
429        * note that when an initial value is not given then ~R = ~D
430        * if iterable empty & no initial value given, return a sentinel value of type ~S
431        * traditional FP type order given for function f
432        * folds in natural FIFO Order
433
434        """
435        return self._ca.foldR(f, initial=initial)
Fold Right
  • reduce right with f using an optional initial value
  • note that ~S can be the same type as either ~R or ~D
  • note that when an initial value is not given then ~R = ~D
  • if iterable empty & no initial value given, return a sentinel value of type ~S
  • traditional FP type order given for function f
  • folds in natural FIFO Order
def map( self, f: Callable[[~D], ~U]) -> DoubleQueue[~U, ~S]:
437    def map(self, f: Callable[[D], U]) -> DoubleQueue[U, S]:
438        """
439        ##### Map DoubleQueue
440
441        Map the function `f` over the DoubleQueue, oldest to newest. Retain
442        original order.
443
444        """
445        return DoubleQueue(*map(f, self._ca), s=self._sentinel)
Map DoubleQueue

Map the function f over the DoubleQueue, oldest to newest. Retain original order.

Inherited Members
QueueBase
QueueBase
 75class FIFOQueue(QueueBase[D, S]):
 76    """
 77    #### FIFO Queue
 78
 79    * stateful First In First Out (FIFO) data structure.
 80    * will resize itself larger as needed
 81    * initial data pushed on in natural FIFO order
 82    """
 83    __slots__ = ()
 84
 85    def __iter__(self) -> Iterator[D]:
 86        return iter(list(self._ca))
 87
 88    def copy(self) -> FIFOQueue[D, S]:
 89        """
 90        ##### FIFOQueue Copy
 91
 92        Return shallow copy of the FIFOQueue.
 93
 94        """
 95        return FIFOQueue(*self._ca, s=self._sentinel)
 96
 97    def __str__(self) -> str:
 98        return "<< " + " < ".join(map(str, self)) + " <<"
 99
100    def push(self, *ds: D) -> None:
101        """
102        ##### Push Data
103
104        Push data onto FIFOQueue. Like Python list, does not return
105        a reference to itself.
106        """
107        self._ca.pushR(*ds)
108
109    def pop(self) -> D|S:
110        """
111        ##### Pop Data
112
113        Pop data off of FIFOQueue. Return sentinel value if queue is empty.
114        """
115        if self._ca:
116            return self._ca.popL()
117        else:
118            return self._sentinel
119
120    def peak_last_in(self) -> D|S:
121        """
122        ### Peak Last In
123
124        Without consuming it, if it is still in the queue, return last element
125        pushed. If queue empty, return sentinel value.
126        """
127        if self._ca:
128            return self._ca[-1]
129        else:
130            return self._sentinel
131
132    def peak_next_out(self) -> D|S:
133        """
134        ### Peak Next Out
135
136        Without consuming it, if the queue is not empty, return the next item
137        ready to be popped. Otherwise, return sentinel value.
138        """
139        if self._ca:
140            return self._ca[0]
141        else:
142            return self._sentinel
143
144    @overload
145    def fold(self, f: Callable[[L, D], L], initial: Optional[L]) -> L|S:
146        ...
147    @overload
148    def fold(self, f: Callable[[D, D], D]) -> D|S:
149        ...
150    @overload
151    def fold(self, f: Callable[[L, D], L], initial: L) -> L:
152        ...
153    @overload
154    def fold(self, f: Callable[[D, D], D], initial: D) -> D:
155        ...
156
157    def fold(self, f: Callable[[L, D], L], initial: Optional[L]=None) -> L|S:
158        """
159        ##### Fold in FIFO Order
160
161        * reduce with `f` using an optional initial value
162        * folds in natural FIFO Order (oldest to newest)
163        * note that ~S can be the same type as either ~L or ~D
164        * note that when an initial value is not given then ~L = ~D
165        * if iterable empty & no initial value given, return a sentinel value of type ~S
166        * traditional FP type order given for function f
167
168        """
169        if initial is None:
170            if not self:
171                return self._sentinel
172        return self._ca.foldL(f, initial=initial)
173
174    def map(self, f: Callable[[D], U]) -> FIFOQueue[U, S]:
175        """
176        ##### Map FIFOQueue
177
178        Map the function `f` over the FIFOQueue, oldest to newest. Retain
179        original order.
180
181        """
182        return FIFOQueue(*map(f, self._ca), s=self._sentinel)

FIFO Queue

  • stateful First In First Out (FIFO) data structure.
  • will resize itself larger as needed
  • initial data pushed on in natural FIFO order
def copy(self) -> FIFOQueue[~D, ~S]:
88    def copy(self) -> FIFOQueue[D, S]:
89        """
90        ##### FIFOQueue Copy
91
92        Return shallow copy of the FIFOQueue.
93
94        """
95        return FIFOQueue(*self._ca, s=self._sentinel)
FIFOQueue Copy

Return shallow copy of the FIFOQueue.

def push(self, *ds: ~D) -> None:
100    def push(self, *ds: D) -> None:
101        """
102        ##### Push Data
103
104        Push data onto FIFOQueue. Like Python list, does not return
105        a reference to itself.
106        """
107        self._ca.pushR(*ds)
Push Data

Push data onto FIFOQueue. Like Python list, does not return a reference to itself.

def pop(self) -> Union[~D, ~S]:
109    def pop(self) -> D|S:
110        """
111        ##### Pop Data
112
113        Pop data off of FIFOQueue. Return sentinel value if queue is empty.
114        """
115        if self._ca:
116            return self._ca.popL()
117        else:
118            return self._sentinel
Pop Data

Pop data off of FIFOQueue. Return sentinel value if queue is empty.

def peak_last_in(self) -> Union[~D, ~S]:
120    def peak_last_in(self) -> D|S:
121        """
122        ### Peak Last In
123
124        Without consuming it, if it is still in the queue, return last element
125        pushed. If queue empty, return sentinel value.
126        """
127        if self._ca:
128            return self._ca[-1]
129        else:
130            return self._sentinel

Peak Last In

Without consuming it, if it is still in the queue, return last element pushed. If queue empty, return sentinel value.

def peak_next_out(self) -> Union[~D, ~S]:
132    def peak_next_out(self) -> D|S:
133        """
134        ### Peak Next Out
135
136        Without consuming it, if the queue is not empty, return the next item
137        ready to be popped. Otherwise, return sentinel value.
138        """
139        if self._ca:
140            return self._ca[0]
141        else:
142            return self._sentinel

Peak Next Out

Without consuming it, if the queue is not empty, return the next item ready to be popped. Otherwise, return sentinel value.

def fold( self, f: Callable[[~L, ~D], ~L], initial: Optional[~L] = None) -> Union[~L, ~S]:
157    def fold(self, f: Callable[[L, D], L], initial: Optional[L]=None) -> L|S:
158        """
159        ##### Fold in FIFO Order
160
161        * reduce with `f` using an optional initial value
162        * folds in natural FIFO Order (oldest to newest)
163        * note that ~S can be the same type as either ~L or ~D
164        * note that when an initial value is not given then ~L = ~D
165        * if iterable empty & no initial value given, return a sentinel value of type ~S
166        * traditional FP type order given for function f
167
168        """
169        if initial is None:
170            if not self:
171                return self._sentinel
172        return self._ca.foldL(f, initial=initial)
Fold in FIFO Order
  • reduce with f using an optional initial value
  • folds in natural FIFO Order (oldest to newest)
  • note that ~S can be the same type as either ~L or ~D
  • note that when an initial value is not given then ~L = ~D
  • if iterable empty & no initial value given, return a sentinel value of type ~S
  • traditional FP type order given for function f
def map( self, f: Callable[[~D], ~U]) -> FIFOQueue[~U, ~S]:
174    def map(self, f: Callable[[D], U]) -> FIFOQueue[U, S]:
175        """
176        ##### Map FIFOQueue
177
178        Map the function `f` over the FIFOQueue, oldest to newest. Retain
179        original order.
180
181        """
182        return FIFOQueue(*map(f, self._ca), s=self._sentinel)
Map FIFOQueue

Map the function f over the FIFOQueue, oldest to newest. Retain original order.

Inherited Members
QueueBase
QueueBase
184class LIFOQueue(QueueBase[D, S]):
185    """
186    #### LIFO Queue
187
188    * Last In First Out (LIFO) stateful queue data structure.
189    * will resize itself larger as needed
190    * initial data pushed on in natural LIFO order
191
192    """
193    __slots__ = ()
194
195    def __iter__(self) -> Iterator[D]:
196        return reversed(list(self._ca))
197
198    def copy(self) -> LIFOQueue[D, S]:
199        """
200        ##### LIFOQueue Copy
201
202        Return shallow copy of the LIFOQueue.
203        """
204        return LIFOQueue(*reversed(self._ca), s=self._sentinel)
205
206    def __str__(self) -> str:
207        return "|| " + " > ".join(map(str, self)) + " ><"
208
209    def push(self, *ds: D) -> None:
210        """
211        ##### Push Data
212
213        Push data on LIFOQueue. Like Python list, does not return
214        a reference to itself.
215        """
216        self._ca.pushR(*ds)
217
218    def pop(self) -> D|S:
219        """
220        ##### Pop LIFO Queue
221
222        Pop data off of LIFOQueue. Return sentinel value if queue is empty.
223        """
224        if self._ca:
225            return self._ca.popR()
226        else:
227            return self._sentinel
228
229    def peak(self) -> D|S:
230        """
231        ##### Peak Next Out
232
233        Without consuming it, if the queue is not empty, return the next item
234        ready to be popped. Otherwise, return sentinel value.
235
236        """
237        if self._ca:
238            return self._ca[-1]
239        else:
240            return self._sentinel
241
242    @overload
243    def fold(self, f: Callable[[D, R], R], initial: Optional[R]) -> R|S:
244        ...
245    @overload
246    def fold(self, f: Callable[[D, D], D]) -> D|S:
247        ...
248    @overload
249    def fold(self, f: Callable[[D, R], R], initial: R) -> R:
250        ...
251    @overload
252    def fold(self, f: Callable[[D, D], D], initial: D) -> D:
253        ...
254
255    def fold(self, f: Callable[[D, R], R], initial: Optional[R]=None) -> R|S:
256        """
257        ##### Fold in LIFO Order
258
259        * reduce with `f` using an optional initial value
260        * folds in natural LIFO Order (newest to oldest)
261        * note that ~S can be the same type as either ~L or ~D
262        * note that when an initial value is not given then ~L = ~D
263        * if iterable empty & no initial value given, return a sentinel value of type ~S
264        * traditional FP type order given for function f
265
266        """
267        if initial is None:
268            if not self:
269                return self._sentinel
270        return self._ca.foldR(f, initial=initial)
271
272    def map(self, f: Callable[[D], U]) -> LIFOQueue[U, S]:
273        """
274        ##### Map LIFOQueue
275
276        Map the function `f` over the LIFOQueue, newest to oldest. Retain
277        original order.
278
279        """
280        return LIFOQueue(*reversed(CA(*map(f, reversed(self._ca)))), s=self._sentinel)

LIFO Queue

  • Last In First Out (LIFO) stateful queue data structure.
  • will resize itself larger as needed
  • initial data pushed on in natural LIFO order
def copy(self) -> LIFOQueue[~D, ~S]:
198    def copy(self) -> LIFOQueue[D, S]:
199        """
200        ##### LIFOQueue Copy
201
202        Return shallow copy of the LIFOQueue.
203        """
204        return LIFOQueue(*reversed(self._ca), s=self._sentinel)
LIFOQueue Copy

Return shallow copy of the LIFOQueue.

def push(self, *ds: ~D) -> None:
209    def push(self, *ds: D) -> None:
210        """
211        ##### Push Data
212
213        Push data on LIFOQueue. Like Python list, does not return
214        a reference to itself.
215        """
216        self._ca.pushR(*ds)
Push Data

Push data on LIFOQueue. Like Python list, does not return a reference to itself.

def pop(self) -> Union[~D, ~S]:
218    def pop(self) -> D|S:
219        """
220        ##### Pop LIFO Queue
221
222        Pop data off of LIFOQueue. Return sentinel value if queue is empty.
223        """
224        if self._ca:
225            return self._ca.popR()
226        else:
227            return self._sentinel
Pop LIFO Queue

Pop data off of LIFOQueue. Return sentinel value if queue is empty.

def peak(self) -> Union[~D, ~S]:
229    def peak(self) -> D|S:
230        """
231        ##### Peak Next Out
232
233        Without consuming it, if the queue is not empty, return the next item
234        ready to be popped. Otherwise, return sentinel value.
235
236        """
237        if self._ca:
238            return self._ca[-1]
239        else:
240            return self._sentinel
Peak Next Out

Without consuming it, if the queue is not empty, return the next item ready to be popped. Otherwise, return sentinel value.

def fold( self, f: Callable[[~D, ~R], ~R], initial: Optional[~R] = None) -> Union[~R, ~S]:
255    def fold(self, f: Callable[[D, R], R], initial: Optional[R]=None) -> R|S:
256        """
257        ##### Fold in LIFO Order
258
259        * reduce with `f` using an optional initial value
260        * folds in natural LIFO Order (newest to oldest)
261        * note that ~S can be the same type as either ~L or ~D
262        * note that when an initial value is not given then ~L = ~D
263        * if iterable empty & no initial value given, return a sentinel value of type ~S
264        * traditional FP type order given for function f
265
266        """
267        if initial is None:
268            if not self:
269                return self._sentinel
270        return self._ca.foldR(f, initial=initial)
Fold in LIFO Order
  • reduce with f using an optional initial value
  • folds in natural LIFO Order (newest to oldest)
  • note that ~S can be the same type as either ~L or ~D
  • note that when an initial value is not given then ~L = ~D
  • if iterable empty & no initial value given, return a sentinel value of type ~S
  • traditional FP type order given for function f
def map( self, f: Callable[[~D], ~U]) -> LIFOQueue[~U, ~S]:
272    def map(self, f: Callable[[D], U]) -> LIFOQueue[U, S]:
273        """
274        ##### Map LIFOQueue
275
276        Map the function `f` over the LIFOQueue, newest to oldest. Retain
277        original order.
278
279        """
280        return LIFOQueue(*reversed(CA(*map(f, reversed(self._ca)))), s=self._sentinel)
Map LIFOQueue

Map the function f over the LIFOQueue, newest to oldest. Retain original order.

Inherited Members
QueueBase
QueueBase
class QueueBase(typing.Generic[~D, ~S]):
42class QueueBase(Generic[D, S]):
43    """
44    #### Base class for queues
45
46    * primarily for DRY implementation inheritance
47    * each queue object "has-a" (contains) a circular array to store its data
48    * len() returns the current number of elements in the queue
49    * in a boolean context, returns true if not empty
50
51    """
52    __slots__ = '_ca', '_sentinel'
53
54    def __init__(self, *ds: D, s: S):
55        self._ca = CA(*ds)
56        self._sentinel = s
57
58    def __repr__(self) -> str:
59        if len(self) == 0:
60            return type(self).__name__ + '(s=' + repr(self._sentinel)+ ')'
61        else:
62            return type(self).__name__ + '(' + ', '.join(map(repr, self._ca)) + ', s=' + repr(self._sentinel)+ ')'
63
64    def __bool__(self) -> bool:
65        return len(self._ca) > 0
66
67    def __len__(self) -> int:
68        return len(self._ca)
69
70    def __eq__(self, other: object) -> bool:
71        if not isinstance(other, type(self)):
72            return False
73        return self._ca == other._ca

Base class for queues

  • primarily for DRY implementation inheritance
  • each queue object "has-a" (contains) a circular array to store its data
  • len() returns the current number of elements in the queue
  • in a boolean context, returns true if not empty
QueueBase(*ds: ~D, s: ~S)
54    def __init__(self, *ds: D, s: S):
55        self._ca = CA(*ds)
56        self._sentinel = s