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 larger as needed

Queue types and factory functions:

  • class FIFOQueue: First-In-First-Out Queue
  • class LIFOQueue: Last-In-First-Out Queue
  • class DoubleQueue: Double-Ended Queue
  • function FQ: Constructs a FIFOQueue from a variable number of arguments
  • function LQ: Constructs a LIFOQueue from a variable number of arguments
  • function DQ: Constructs a DoubleQueue from a variable number of arguments
  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"""### Queue based datastructures.
 16
 17* stateful queue data structures with amortized O(1) pushes and pops each end
 18* obtaining length (number of elements) of a queue is an O(1) operation
 19* implemented in a "has-a" relationship with a Python list based circular array
 20* these data structures will resize themselves larger as needed
 21
 22#### Queue types and factory functions:
 23
 24* class **FIFOQueue:** First-In-First-Out Queue
 25* class **LIFOQueue:** Last-In-First-Out Queue
 26* class **DoubleQueue:** Double-Ended Queue
 27* function **FQ:** Constructs a FIFOQueue from a variable number of arguments
 28* function **LQ:** Constructs a LIFOQueue from a variable number of arguments
 29* function **DQ:** Constructs a DoubleQueue from a variable number of arguments
 30
 31"""
 32from __future__ import annotations
 33
 34from collections.abc import Callable, Iterable, Iterator, Sequence
 35from typing import Never, overload
 36from grscheller.circular_array.ca import ca, CA
 37from grscheller.fp.err_handling import MB
 38
 39__all__ = [ 'DoubleQueue', 'FIFOQueue', 'LIFOQueue', 'QueueBase',
 40            'DQ', 'FQ', 'LQ' ]
 41
 42class QueueBase[D](Sequence[D]):
 43    """Base class for circular area based queues.
 44
 45    * implemented with a grscheller.circular-array in a "has-a" relationship
 46    * order of initial data retained
 47    * slicing not yet implemented
 48
 49    """
 50    __slots__ = '_ca'
 51
 52    def __init__(self, *dss: Iterable[D]) -> None:
 53        if len(dss) < 2:
 54            self._ca = ca(*dss)
 55        else:
 56            msg1 = f'{type(self).__name__}: expected at most 1 '
 57            msg2 = f'iterable argument, got {len(dss)}.'
 58            raise TypeError(msg1+msg2)
 59
 60    def __bool__(self) -> bool:
 61        return len(self._ca) > 0
 62
 63    def __len__(self) -> int:
 64        return len(self._ca)
 65
 66    def __eq__(self, other: object, /) -> bool:
 67        if not isinstance(other, type(self)):
 68            return False
 69        return self._ca == other._ca
 70
 71    @overload
 72    def __getitem__(self, idx: int, /) -> D: ...
 73    @overload
 74    def __getitem__(self, idx: slice, /) -> Sequence[D]: ...
 75
 76    def __getitem__(self, idx: int|slice, /) -> D|Sequence[D]|Never:
 77        if isinstance(idx, slice):
 78            raise NotImplementedError
 79        return self._ca[idx]
 80
 81class FIFOQueue[D](QueueBase[D]):
 82    """FIFO Queue
 83
 84    * stateful First-In-First-Out (FIFO) data structure
 85    * initial data pushed on in natural FIFO order
 86
 87    """
 88    __slots__ = ()
 89
 90    def __iter__(self) -> Iterator[D]:
 91        return iter(list(self._ca))
 92
 93    def __repr__(self) -> str:
 94        if len(self) == 0:
 95            return 'FQ()'
 96        else:
 97            return 'FQ(' + ', '.join(map(repr, self._ca)) + ')'
 98
 99    def __str__(self) -> str:
100        return "<< " + " < ".join(map(str, self)) + " <<"
101
102    def copy(self) -> FIFOQueue[D]:
103        """Return a shallow copy of the `FIFOQueue`."""
104        return FIFOQueue(self._ca)
105
106    def push(self, *ds: D) -> None:
107        """Push data onto `FIFOQueue`.
108
109        * like a Python List, does not return a value
110
111        """
112        self._ca.pushR(*ds)
113
114    def pop(self) -> MB[D]:
115        """Pop data from `FIFOQueue`.
116
117        * pop item off queue, return item in a maybe monad
118        * returns an empty `MB()` if queue is empty
119
120        """
121        if self._ca:
122            return MB(self._ca.popL())
123        else:
124            return MB()
125
126    def peak_last_in(self) -> MB[D]:
127        """Peak last data into `FIFOQueue`.
128
129        * return a maybe monad of the last item pushed to queue
130        * does not consume the data
131        * if item already popped, return `MB()`
132
133        """
134        if self._ca:
135            return MB(self._ca[-1])
136        else:
137            return MB()
138
139    def peak_next_out(self) -> MB[D]:
140        """Peak next data out of `FIFOQueue`.
141
142        * returns a maybe monad of the next item to be popped from the queue.
143        * does not consume it the item
144        * returns `MB()` if queue is empty
145
146        """
147        if self._ca:
148            return MB(self._ca[0])
149        else:
150            return MB()
151
152    def fold[L](self, f: Callable[[L, D], L], initial: L|None=None, /) -> MB[L]:
153        """Fold `FIFOQueue` in natural order.
154
155        Reduce with `f` using an optional initial value.
156
157        * folds in natural FIFO Order (oldest to newest)
158        * note that when an initial value is not given then `~L = ~D`
159        * if iterable empty & no initial value given, return `MB()`
160        * traditional FP type order given for function `f`
161
162        """
163        if initial is None:
164            if not self._ca:
165                return MB()
166        return MB(self._ca.foldL(f, initial=initial))
167
168    def map[U](self, f: Callable[[D], U], /) -> FIFOQueue[U]:
169        """Map over the `FIFOQueue`.
170
171        * map function `f` over the queue
172          * oldest to newest
173          * retain original order
174        * returns a new instance
175
176        """
177        return FIFOQueue(map(f, self._ca))
178
179class LIFOQueue[D](QueueBase[D]):
180    """LIFO Queue.
181
182    * stateful Last-In-First-Out (LIFO) data structure
183    * initial data pushed on in natural LIFO order
184
185    """
186    __slots__ = ()
187
188    def __iter__(self) -> Iterator[D]:
189        return reversed(list(self._ca))
190
191    def __repr__(self) -> str:
192        if len(self) == 0:
193            return 'LQ()'
194        else:
195            return 'LQ(' + ', '.join(map(repr, self._ca)) + ')'
196
197    def __str__(self) -> str:
198        return "|| " + " > ".join(map(str, self)) + " ><"
199
200    def copy(self) -> LIFOQueue[D]:
201        """Return a shallow copy of the `LIFOQueue`."""
202        return LIFOQueue(reversed(self._ca))
203
204    def push(self, *ds: D) -> None:
205        """Push data onto `LIFOQueue`.
206
207        * like a Python List, does not return a value
208
209        """
210        self._ca.pushR(*ds)
211
212    def pop(self) -> MB[D]:
213        """Pop data from `LIFOQueue`.
214
215        * pop item off of queue, return item in a maybe monad
216        * returns an empty `MB()` if queue is empty
217
218        """
219        if self._ca:
220            return MB(self._ca.popR())
221        else:
222            return MB()
223
224    def peak(self) -> MB[D]:
225        """Peak next data out of `LIFOQueue`.
226
227        * return a maybe monad of the next item to be popped from the queue
228        * does not consume the item
229        * returns `MB()` if queue is empty
230
231        """
232        if self._ca:
233            return MB(self._ca[-1])
234        else:
235            return MB()
236
237    def fold[R](self, f: Callable[[D, R], R], initial: R|None=None, /) -> MB[R]:
238        """Fold `LIFOQueue` in natural order.
239
240        Reduce with `f` using an optional initial value.
241
242        * folds in natural LIFO Order (newest to oldest)
243        * note that when an initial value is not given then `~R = ~D`
244        * if iterable empty & no initial value given, return `MB()`
245        * traditional FP type order given for function `f`
246
247        """
248        if initial is None:
249            if not self._ca:
250                return MB()
251        return MB(self._ca.foldR(f, initial=initial))
252
253    def map[U](self, f: Callable[[D], U], /) -> LIFOQueue[U]:
254        """Map Over the `LIFOQueue`.
255
256        * map the function `f` over the queue
257          * newest to oldest
258          * retain original order
259        * returns a new instance
260
261        """
262        return LIFOQueue(reversed(CA(*map(f, reversed(self._ca)))))
263
264class DoubleQueue[D](QueueBase[D]):
265    """Double Ended Queue
266
267    * stateful Double-Ended (DEQueue) data structure
268    * order of initial data retained
269
270    """
271    __slots__ = ()
272
273    def __iter__(self) -> Iterator[D]:
274        return iter(list(self._ca))
275
276    def __reversed__(self) -> Iterator[D]:
277        return reversed(list(self._ca))
278
279    def __repr__(self) -> str:
280        if len(self) == 0:
281            return 'DQ()'
282        else:
283            return 'DQ(' + ', '.join(map(repr, self._ca)) + ')'
284
285    def __str__(self) -> str:
286        return ">< " + " | ".join(map(str, self)) + " ><"
287
288    def copy(self) -> DoubleQueue[D]:
289        """Return a shallow copy of the `DoubleQueue`."""
290        return DoubleQueue(self._ca)
291
292    def pushL(self, *ds: D) -> None:
293        """Push data onto left side (front) of `DoubleQueue`.
294
295        * like a Python List, does not return a value
296
297        """
298        self._ca.pushL(*ds)
299
300    def pushR(self, *ds: D) -> None:
301        """Push data onto right side (rear) of `DoubleQueue`.
302
303        * like a Python List, does not return a value
304
305        """
306        self._ca.pushR(*ds)
307
308    def popL(self) -> MB[D]:
309        """Pop Data from left side (front) of `DoubleQueue`.
310
311        * pop left most item off of queue, return item in a maybe monad
312        * returns an empty `MB()` if queue is empty
313
314        """
315        if self._ca:
316            return MB(self._ca.popL())
317        else:
318            return MB()
319
320    def popR(self) -> MB[D]:
321        """Pop Data from right side (rear) of `DoubleQueue`.
322
323        * pop right most item off of queue, return item in a maybe monad
324        * returns an empty `MB()` if queue is empty
325
326        """
327        if self._ca:
328            return MB(self._ca.popR())
329        else:
330            return MB()
331
332    def peakL(self) -> MB[D]:
333        """Peak left side of `DoubleQueue`.
334
335        * return left most value in a maybe monad
336        * does not consume the item
337        * returns an empty `MB()` if queue is empty
338
339        """
340        if self._ca:
341            return MB(self._ca[0])
342        else:
343            return MB()
344
345    def peakR(self) -> MB[D]:
346        """Peak right side of `DoubleQueue`.
347
348        * return right most value in a maybe monad
349        * does not consume the item
350        * returns an empty `MB()` if queue is empty
351
352        """
353        if self._ca:
354            return MB(self._ca[-1])
355        else:
356            return MB()
357
358    def foldL[L](self, f: Callable[[L, D], L], initial: L|None=None, /) -> MB[L]:
359        """Fold `DoubleQueue` left to right.
360
361        Reduce left with `f` using an optional initial value.
362
363        * note that when an initial value is not given then `~L = ~D`
364        * if iterable empty & no initial value given, return `MB()`
365        * traditional FP type order given for function `f`
366
367        """
368        if initial is None:
369            if not self._ca:
370                return MB()
371        return MB(self._ca.foldL(f, initial=initial))
372
373    def foldR[R](self, f: Callable[[D, R], R], initial: R|None=None, /) -> MB[R]:
374        """Fold `DoubleQueue` right to left.
375
376        Reduce right with `f` using an optional initial value.
377
378        * note that when an initial value is not given then `~R = ~D`
379        * if iterable empty & no initial value given, return `MB()`
380        * traditional FP type order given for function `f`
381
382        """
383        if initial is None:
384            if not self._ca:
385                return MB()
386        return MB(self._ca.foldR(f, initial=initial))
387
388    def map[U](self, f: Callable[[D], U], /) -> DoubleQueue[U]:
389        """`Map a function over `DoubleQueue`.
390
391        * map the function `f` over the `DoubleQueue`
392          * left to right
393          * retain original order
394        * returns a new instance
395
396        """
397        return DoubleQueue(map(f, self._ca))
398
399def FQ[D](*ds: D) -> FIFOQueue[D]:
400    """Return a FIFOQueue where data is pushed on in natural FIFO order."""
401    return FIFOQueue(ds)
402
403def LQ[D](*ds: D) -> LIFOQueue[D]:
404    """Return a LIFOQueue where data is pushed on in natural LIFO order."""
405    return LIFOQueue(ds)
406
407def DQ[D](*ds: D) -> DoubleQueue[D]:
408    """Return a DoubleQueue whose data is pushed on from the right."""
409    return DoubleQueue(ds)
class DoubleQueue(grscheller.datastructures.queues.QueueBase[D], typing.Generic[D]):
265class DoubleQueue[D](QueueBase[D]):
266    """Double Ended Queue
267
268    * stateful Double-Ended (DEQueue) data structure
269    * order of initial data retained
270
271    """
272    __slots__ = ()
273
274    def __iter__(self) -> Iterator[D]:
275        return iter(list(self._ca))
276
277    def __reversed__(self) -> Iterator[D]:
278        return reversed(list(self._ca))
279
280    def __repr__(self) -> str:
281        if len(self) == 0:
282            return 'DQ()'
283        else:
284            return 'DQ(' + ', '.join(map(repr, self._ca)) + ')'
285
286    def __str__(self) -> str:
287        return ">< " + " | ".join(map(str, self)) + " ><"
288
289    def copy(self) -> DoubleQueue[D]:
290        """Return a shallow copy of the `DoubleQueue`."""
291        return DoubleQueue(self._ca)
292
293    def pushL(self, *ds: D) -> None:
294        """Push data onto left side (front) of `DoubleQueue`.
295
296        * like a Python List, does not return a value
297
298        """
299        self._ca.pushL(*ds)
300
301    def pushR(self, *ds: D) -> None:
302        """Push data onto right side (rear) of `DoubleQueue`.
303
304        * like a Python List, does not return a value
305
306        """
307        self._ca.pushR(*ds)
308
309    def popL(self) -> MB[D]:
310        """Pop Data from left side (front) of `DoubleQueue`.
311
312        * pop left most item off of queue, return item in a maybe monad
313        * returns an empty `MB()` if queue is empty
314
315        """
316        if self._ca:
317            return MB(self._ca.popL())
318        else:
319            return MB()
320
321    def popR(self) -> MB[D]:
322        """Pop Data from right side (rear) of `DoubleQueue`.
323
324        * pop right most item off of queue, return item in a maybe monad
325        * returns an empty `MB()` if queue is empty
326
327        """
328        if self._ca:
329            return MB(self._ca.popR())
330        else:
331            return MB()
332
333    def peakL(self) -> MB[D]:
334        """Peak left side of `DoubleQueue`.
335
336        * return left most value in a maybe monad
337        * does not consume the item
338        * returns an empty `MB()` if queue is empty
339
340        """
341        if self._ca:
342            return MB(self._ca[0])
343        else:
344            return MB()
345
346    def peakR(self) -> MB[D]:
347        """Peak right side of `DoubleQueue`.
348
349        * return right most value in a maybe monad
350        * does not consume the item
351        * returns an empty `MB()` if queue is empty
352
353        """
354        if self._ca:
355            return MB(self._ca[-1])
356        else:
357            return MB()
358
359    def foldL[L](self, f: Callable[[L, D], L], initial: L|None=None, /) -> MB[L]:
360        """Fold `DoubleQueue` left to right.
361
362        Reduce left with `f` using an optional initial value.
363
364        * note that when an initial value is not given then `~L = ~D`
365        * if iterable empty & no initial value given, return `MB()`
366        * traditional FP type order given for function `f`
367
368        """
369        if initial is None:
370            if not self._ca:
371                return MB()
372        return MB(self._ca.foldL(f, initial=initial))
373
374    def foldR[R](self, f: Callable[[D, R], R], initial: R|None=None, /) -> MB[R]:
375        """Fold `DoubleQueue` right to left.
376
377        Reduce right with `f` using an optional initial value.
378
379        * note that when an initial value is not given then `~R = ~D`
380        * if iterable empty & no initial value given, return `MB()`
381        * traditional FP type order given for function `f`
382
383        """
384        if initial is None:
385            if not self._ca:
386                return MB()
387        return MB(self._ca.foldR(f, initial=initial))
388
389    def map[U](self, f: Callable[[D], U], /) -> DoubleQueue[U]:
390        """`Map a function over `DoubleQueue`.
391
392        * map the function `f` over the `DoubleQueue`
393          * left to right
394          * retain original order
395        * returns a new instance
396
397        """
398        return DoubleQueue(map(f, self._ca))

Double Ended Queue

  • stateful Double-Ended (DEQueue) data structure
  • order of initial data retained
def copy(self) -> 'DoubleQueue[D]':
289    def copy(self) -> DoubleQueue[D]:
290        """Return a shallow copy of the `DoubleQueue`."""
291        return DoubleQueue(self._ca)

Return a shallow copy of the DoubleQueue.

def pushL(self, *ds: 'D') -> None:
293    def pushL(self, *ds: D) -> None:
294        """Push data onto left side (front) of `DoubleQueue`.
295
296        * like a Python List, does not return a value
297
298        """
299        self._ca.pushL(*ds)

Push data onto left side (front) of DoubleQueue.

  • like a Python List, does not return a value
def pushR(self, *ds: 'D') -> None:
301    def pushR(self, *ds: D) -> None:
302        """Push data onto right side (rear) of `DoubleQueue`.
303
304        * like a Python List, does not return a value
305
306        """
307        self._ca.pushR(*ds)

Push data onto right side (rear) of DoubleQueue.

  • like a Python List, does not return a value
def popL(self) -> 'MB[D]':
309    def popL(self) -> MB[D]:
310        """Pop Data from left side (front) of `DoubleQueue`.
311
312        * pop left most item off of queue, return item in a maybe monad
313        * returns an empty `MB()` if queue is empty
314
315        """
316        if self._ca:
317            return MB(self._ca.popL())
318        else:
319            return MB()

Pop Data from left side (front) of DoubleQueue.

  • pop left most item off of queue, return item in a maybe monad
  • returns an empty MB() if queue is empty
def popR(self) -> 'MB[D]':
321    def popR(self) -> MB[D]:
322        """Pop Data from right side (rear) of `DoubleQueue`.
323
324        * pop right most item off of queue, return item in a maybe monad
325        * returns an empty `MB()` if queue is empty
326
327        """
328        if self._ca:
329            return MB(self._ca.popR())
330        else:
331            return MB()

Pop Data from right side (rear) of DoubleQueue.

  • pop right most item off of queue, return item in a maybe monad
  • returns an empty MB() if queue is empty
def peakL(self) -> 'MB[D]':
333    def peakL(self) -> MB[D]:
334        """Peak left side of `DoubleQueue`.
335
336        * return left most value in a maybe monad
337        * does not consume the item
338        * returns an empty `MB()` if queue is empty
339
340        """
341        if self._ca:
342            return MB(self._ca[0])
343        else:
344            return MB()

Peak left side of DoubleQueue.

  • return left most value in a maybe monad
  • does not consume the item
  • returns an empty MB() if queue is empty
def peakR(self) -> 'MB[D]':
346    def peakR(self) -> MB[D]:
347        """Peak right side of `DoubleQueue`.
348
349        * return right most value in a maybe monad
350        * does not consume the item
351        * returns an empty `MB()` if queue is empty
352
353        """
354        if self._ca:
355            return MB(self._ca[-1])
356        else:
357            return MB()

Peak right side of DoubleQueue.

  • return right most value in a maybe monad
  • does not consume the item
  • returns an empty MB() if queue is empty
def foldL(self, f: 'Callable[[L, D], L]', initial: 'L | None' = None, /) -> 'MB[L]':
359    def foldL[L](self, f: Callable[[L, D], L], initial: L|None=None, /) -> MB[L]:
360        """Fold `DoubleQueue` left to right.
361
362        Reduce left with `f` using an optional initial value.
363
364        * note that when an initial value is not given then `~L = ~D`
365        * if iterable empty & no initial value given, return `MB()`
366        * traditional FP type order given for function `f`
367
368        """
369        if initial is None:
370            if not self._ca:
371                return MB()
372        return MB(self._ca.foldL(f, initial=initial))

Fold DoubleQueue left to right.

Reduce left with f using an optional initial value.

  • note that when an initial value is not given then ~L = ~D
  • if iterable empty & no initial value given, return MB()
  • traditional FP type order given for function f
def foldR(self, f: 'Callable[[D, R], R]', initial: 'R | None' = None, /) -> 'MB[R]':
374    def foldR[R](self, f: Callable[[D, R], R], initial: R|None=None, /) -> MB[R]:
375        """Fold `DoubleQueue` right to left.
376
377        Reduce right with `f` using an optional initial value.
378
379        * note that when an initial value is not given then `~R = ~D`
380        * if iterable empty & no initial value given, return `MB()`
381        * traditional FP type order given for function `f`
382
383        """
384        if initial is None:
385            if not self._ca:
386                return MB()
387        return MB(self._ca.foldR(f, initial=initial))

Fold DoubleQueue right to left.

Reduce right with f using an optional initial value.

  • note that when an initial value is not given then ~R = ~D
  • if iterable empty & no initial value given, return MB()
  • traditional FP type order given for function f
def map(self, f: 'Callable[[D], U]', /) -> 'DoubleQueue[U]':
389    def map[U](self, f: Callable[[D], U], /) -> DoubleQueue[U]:
390        """`Map a function over `DoubleQueue`.
391
392        * map the function `f` over the `DoubleQueue`
393          * left to right
394          * retain original order
395        * returns a new instance
396
397        """
398        return DoubleQueue(map(f, self._ca))

Map a function overDoubleQueue`.

  • map the function f over the DoubleQueue
    • left to right
    • retain original order
  • returns a new instance
Inherited Members
QueueBase
QueueBase
class FIFOQueue(grscheller.datastructures.queues.QueueBase[D], typing.Generic[D]):
 82class FIFOQueue[D](QueueBase[D]):
 83    """FIFO Queue
 84
 85    * stateful First-In-First-Out (FIFO) data structure
 86    * initial data pushed on in natural FIFO order
 87
 88    """
 89    __slots__ = ()
 90
 91    def __iter__(self) -> Iterator[D]:
 92        return iter(list(self._ca))
 93
 94    def __repr__(self) -> str:
 95        if len(self) == 0:
 96            return 'FQ()'
 97        else:
 98            return 'FQ(' + ', '.join(map(repr, self._ca)) + ')'
 99
100    def __str__(self) -> str:
101        return "<< " + " < ".join(map(str, self)) + " <<"
102
103    def copy(self) -> FIFOQueue[D]:
104        """Return a shallow copy of the `FIFOQueue`."""
105        return FIFOQueue(self._ca)
106
107    def push(self, *ds: D) -> None:
108        """Push data onto `FIFOQueue`.
109
110        * like a Python List, does not return a value
111
112        """
113        self._ca.pushR(*ds)
114
115    def pop(self) -> MB[D]:
116        """Pop data from `FIFOQueue`.
117
118        * pop item off queue, return item in a maybe monad
119        * returns an empty `MB()` if queue is empty
120
121        """
122        if self._ca:
123            return MB(self._ca.popL())
124        else:
125            return MB()
126
127    def peak_last_in(self) -> MB[D]:
128        """Peak last data into `FIFOQueue`.
129
130        * return a maybe monad of the last item pushed to queue
131        * does not consume the data
132        * if item already popped, return `MB()`
133
134        """
135        if self._ca:
136            return MB(self._ca[-1])
137        else:
138            return MB()
139
140    def peak_next_out(self) -> MB[D]:
141        """Peak next data out of `FIFOQueue`.
142
143        * returns a maybe monad of the next item to be popped from the queue.
144        * does not consume it the item
145        * returns `MB()` if queue is empty
146
147        """
148        if self._ca:
149            return MB(self._ca[0])
150        else:
151            return MB()
152
153    def fold[L](self, f: Callable[[L, D], L], initial: L|None=None, /) -> MB[L]:
154        """Fold `FIFOQueue` in natural order.
155
156        Reduce with `f` using an optional initial value.
157
158        * folds in natural FIFO Order (oldest to newest)
159        * note that when an initial value is not given then `~L = ~D`
160        * if iterable empty & no initial value given, return `MB()`
161        * traditional FP type order given for function `f`
162
163        """
164        if initial is None:
165            if not self._ca:
166                return MB()
167        return MB(self._ca.foldL(f, initial=initial))
168
169    def map[U](self, f: Callable[[D], U], /) -> FIFOQueue[U]:
170        """Map over the `FIFOQueue`.
171
172        * map function `f` over the queue
173          * oldest to newest
174          * retain original order
175        * returns a new instance
176
177        """
178        return FIFOQueue(map(f, self._ca))

FIFO Queue

  • stateful First-In-First-Out (FIFO) data structure
  • initial data pushed on in natural FIFO order
def copy(self) -> 'FIFOQueue[D]':
103    def copy(self) -> FIFOQueue[D]:
104        """Return a shallow copy of the `FIFOQueue`."""
105        return FIFOQueue(self._ca)

Return a shallow copy of the FIFOQueue.

def push(self, *ds: 'D') -> None:
107    def push(self, *ds: D) -> None:
108        """Push data onto `FIFOQueue`.
109
110        * like a Python List, does not return a value
111
112        """
113        self._ca.pushR(*ds)

Push data onto FIFOQueue.

  • like a Python List, does not return a value
def pop(self) -> 'MB[D]':
115    def pop(self) -> MB[D]:
116        """Pop data from `FIFOQueue`.
117
118        * pop item off queue, return item in a maybe monad
119        * returns an empty `MB()` if queue is empty
120
121        """
122        if self._ca:
123            return MB(self._ca.popL())
124        else:
125            return MB()

Pop data from FIFOQueue.

  • pop item off queue, return item in a maybe monad
  • returns an empty MB() if queue is empty
def peak_last_in(self) -> 'MB[D]':
127    def peak_last_in(self) -> MB[D]:
128        """Peak last data into `FIFOQueue`.
129
130        * return a maybe monad of the last item pushed to queue
131        * does not consume the data
132        * if item already popped, return `MB()`
133
134        """
135        if self._ca:
136            return MB(self._ca[-1])
137        else:
138            return MB()

Peak last data into FIFOQueue.

  • return a maybe monad of the last item pushed to queue
  • does not consume the data
  • if item already popped, return MB()
def peak_next_out(self) -> 'MB[D]':
140    def peak_next_out(self) -> MB[D]:
141        """Peak next data out of `FIFOQueue`.
142
143        * returns a maybe monad of the next item to be popped from the queue.
144        * does not consume it the item
145        * returns `MB()` if queue is empty
146
147        """
148        if self._ca:
149            return MB(self._ca[0])
150        else:
151            return MB()

Peak next data out of FIFOQueue.

  • returns a maybe monad of the next item to be popped from the queue.
  • does not consume it the item
  • returns MB() if queue is empty
def fold(self, f: 'Callable[[L, D], L]', initial: 'L | None' = None, /) -> 'MB[L]':
153    def fold[L](self, f: Callable[[L, D], L], initial: L|None=None, /) -> MB[L]:
154        """Fold `FIFOQueue` in natural order.
155
156        Reduce with `f` using an optional initial value.
157
158        * folds in natural FIFO Order (oldest to newest)
159        * note that when an initial value is not given then `~L = ~D`
160        * if iterable empty & no initial value given, return `MB()`
161        * traditional FP type order given for function `f`
162
163        """
164        if initial is None:
165            if not self._ca:
166                return MB()
167        return MB(self._ca.foldL(f, initial=initial))

Fold FIFOQueue in natural order.

Reduce with f using an optional initial value.

  • folds in natural FIFO Order (oldest to newest)
  • note that when an initial value is not given then ~L = ~D
  • if iterable empty & no initial value given, return MB()
  • traditional FP type order given for function f
def map(self, f: 'Callable[[D], U]', /) -> 'FIFOQueue[U]':
169    def map[U](self, f: Callable[[D], U], /) -> FIFOQueue[U]:
170        """Map over the `FIFOQueue`.
171
172        * map function `f` over the queue
173          * oldest to newest
174          * retain original order
175        * returns a new instance
176
177        """
178        return FIFOQueue(map(f, self._ca))

Map over the FIFOQueue.

  • map function f over the queue
    • oldest to newest
    • retain original order
  • returns a new instance
Inherited Members
QueueBase
QueueBase
class LIFOQueue(grscheller.datastructures.queues.QueueBase[D], typing.Generic[D]):
180class LIFOQueue[D](QueueBase[D]):
181    """LIFO Queue.
182
183    * stateful Last-In-First-Out (LIFO) data structure
184    * initial data pushed on in natural LIFO order
185
186    """
187    __slots__ = ()
188
189    def __iter__(self) -> Iterator[D]:
190        return reversed(list(self._ca))
191
192    def __repr__(self) -> str:
193        if len(self) == 0:
194            return 'LQ()'
195        else:
196            return 'LQ(' + ', '.join(map(repr, self._ca)) + ')'
197
198    def __str__(self) -> str:
199        return "|| " + " > ".join(map(str, self)) + " ><"
200
201    def copy(self) -> LIFOQueue[D]:
202        """Return a shallow copy of the `LIFOQueue`."""
203        return LIFOQueue(reversed(self._ca))
204
205    def push(self, *ds: D) -> None:
206        """Push data onto `LIFOQueue`.
207
208        * like a Python List, does not return a value
209
210        """
211        self._ca.pushR(*ds)
212
213    def pop(self) -> MB[D]:
214        """Pop data from `LIFOQueue`.
215
216        * pop item off of queue, return item in a maybe monad
217        * returns an empty `MB()` if queue is empty
218
219        """
220        if self._ca:
221            return MB(self._ca.popR())
222        else:
223            return MB()
224
225    def peak(self) -> MB[D]:
226        """Peak next data out of `LIFOQueue`.
227
228        * return a maybe monad of the next item to be popped from the queue
229        * does not consume the item
230        * returns `MB()` if queue is empty
231
232        """
233        if self._ca:
234            return MB(self._ca[-1])
235        else:
236            return MB()
237
238    def fold[R](self, f: Callable[[D, R], R], initial: R|None=None, /) -> MB[R]:
239        """Fold `LIFOQueue` in natural order.
240
241        Reduce with `f` using an optional initial value.
242
243        * folds in natural LIFO Order (newest to oldest)
244        * note that when an initial value is not given then `~R = ~D`
245        * if iterable empty & no initial value given, return `MB()`
246        * traditional FP type order given for function `f`
247
248        """
249        if initial is None:
250            if not self._ca:
251                return MB()
252        return MB(self._ca.foldR(f, initial=initial))
253
254    def map[U](self, f: Callable[[D], U], /) -> LIFOQueue[U]:
255        """Map Over the `LIFOQueue`.
256
257        * map the function `f` over the queue
258          * newest to oldest
259          * retain original order
260        * returns a new instance
261
262        """
263        return LIFOQueue(reversed(CA(*map(f, reversed(self._ca)))))

LIFO Queue.

  • stateful Last-In-First-Out (LIFO) data structure
  • initial data pushed on in natural LIFO order
def copy(self) -> 'LIFOQueue[D]':
201    def copy(self) -> LIFOQueue[D]:
202        """Return a shallow copy of the `LIFOQueue`."""
203        return LIFOQueue(reversed(self._ca))

Return a shallow copy of the LIFOQueue.

def push(self, *ds: 'D') -> None:
205    def push(self, *ds: D) -> None:
206        """Push data onto `LIFOQueue`.
207
208        * like a Python List, does not return a value
209
210        """
211        self._ca.pushR(*ds)

Push data onto LIFOQueue.

  • like a Python List, does not return a value
def pop(self) -> 'MB[D]':
213    def pop(self) -> MB[D]:
214        """Pop data from `LIFOQueue`.
215
216        * pop item off of queue, return item in a maybe monad
217        * returns an empty `MB()` if queue is empty
218
219        """
220        if self._ca:
221            return MB(self._ca.popR())
222        else:
223            return MB()

Pop data from LIFOQueue.

  • pop item off of queue, return item in a maybe monad
  • returns an empty MB() if queue is empty
def peak(self) -> 'MB[D]':
225    def peak(self) -> MB[D]:
226        """Peak next data out of `LIFOQueue`.
227
228        * return a maybe monad of the next item to be popped from the queue
229        * does not consume the item
230        * returns `MB()` if queue is empty
231
232        """
233        if self._ca:
234            return MB(self._ca[-1])
235        else:
236            return MB()

Peak next data out of LIFOQueue.

  • return a maybe monad of the next item to be popped from the queue
  • does not consume the item
  • returns MB() if queue is empty
def fold(self, f: 'Callable[[D, R], R]', initial: 'R | None' = None, /) -> 'MB[R]':
238    def fold[R](self, f: Callable[[D, R], R], initial: R|None=None, /) -> MB[R]:
239        """Fold `LIFOQueue` in natural order.
240
241        Reduce with `f` using an optional initial value.
242
243        * folds in natural LIFO Order (newest to oldest)
244        * note that when an initial value is not given then `~R = ~D`
245        * if iterable empty & no initial value given, return `MB()`
246        * traditional FP type order given for function `f`
247
248        """
249        if initial is None:
250            if not self._ca:
251                return MB()
252        return MB(self._ca.foldR(f, initial=initial))

Fold LIFOQueue in natural order.

Reduce with f using an optional initial value.

  • folds in natural LIFO Order (newest to oldest)
  • note that when an initial value is not given then ~R = ~D
  • if iterable empty & no initial value given, return MB()
  • traditional FP type order given for function f
def map(self, f: 'Callable[[D], U]', /) -> 'LIFOQueue[U]':
254    def map[U](self, f: Callable[[D], U], /) -> LIFOQueue[U]:
255        """Map Over the `LIFOQueue`.
256
257        * map the function `f` over the queue
258          * newest to oldest
259          * retain original order
260        * returns a new instance
261
262        """
263        return LIFOQueue(reversed(CA(*map(f, reversed(self._ca)))))

Map Over the LIFOQueue.

  • map the function f over the queue
    • newest to oldest
    • retain original order
  • returns a new instance
Inherited Members
QueueBase
QueueBase
class QueueBase(collections.abc.Sequence[D], typing.Generic[D]):
43class QueueBase[D](Sequence[D]):
44    """Base class for circular area based queues.
45
46    * implemented with a grscheller.circular-array in a "has-a" relationship
47    * order of initial data retained
48    * slicing not yet implemented
49
50    """
51    __slots__ = '_ca'
52
53    def __init__(self, *dss: Iterable[D]) -> None:
54        if len(dss) < 2:
55            self._ca = ca(*dss)
56        else:
57            msg1 = f'{type(self).__name__}: expected at most 1 '
58            msg2 = f'iterable argument, got {len(dss)}.'
59            raise TypeError(msg1+msg2)
60
61    def __bool__(self) -> bool:
62        return len(self._ca) > 0
63
64    def __len__(self) -> int:
65        return len(self._ca)
66
67    def __eq__(self, other: object, /) -> bool:
68        if not isinstance(other, type(self)):
69            return False
70        return self._ca == other._ca
71
72    @overload
73    def __getitem__(self, idx: int, /) -> D: ...
74    @overload
75    def __getitem__(self, idx: slice, /) -> Sequence[D]: ...
76
77    def __getitem__(self, idx: int|slice, /) -> D|Sequence[D]|Never:
78        if isinstance(idx, slice):
79            raise NotImplementedError
80        return self._ca[idx]

Base class for circular area based queues.

  • implemented with a grscheller.circular-array in a "has-a" relationship
  • order of initial data retained
  • slicing not yet implemented
QueueBase(*dss: 'Iterable[D]')
53    def __init__(self, *dss: Iterable[D]) -> None:
54        if len(dss) < 2:
55            self._ca = ca(*dss)
56        else:
57            msg1 = f'{type(self).__name__}: expected at most 1 '
58            msg2 = f'iterable argument, got {len(dss)}.'
59            raise TypeError(msg1+msg2)
def DQ(*ds: 'D') -> 'DoubleQueue[D]':
408def DQ[D](*ds: D) -> DoubleQueue[D]:
409    """Return a DoubleQueue whose data is pushed on from the right."""
410    return DoubleQueue(ds)

Return a DoubleQueue whose data is pushed on from the right.

def FQ(*ds: 'D') -> 'FIFOQueue[D]':
400def FQ[D](*ds: D) -> FIFOQueue[D]:
401    """Return a FIFOQueue where data is pushed on in natural FIFO order."""
402    return FIFOQueue(ds)

Return a FIFOQueue where data is pushed on in natural FIFO order.

def LQ(*ds: 'D') -> 'LIFOQueue[D]':
404def LQ[D](*ds: D) -> LIFOQueue[D]:
405    """Return a LIFOQueue where data is pushed on in natural LIFO order."""
406    return LIFOQueue(ds)

Return a LIFOQueue where data is pushed on in natural LIFO order.