grscheller.datastructures.queues

Queue based data structures

  • 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

FIFOQueue

  • class FIFOQueue: First-In-First-Out Queue
  • function FQ: Constructs a FIFOQueue from a variable number of arguments

LIFOQueue

  • class LIFOQueue: Last-In-First-Out Queue
  • function LQ: Constructs a LIFOQueue from a variable number of arguments

DoubleQueue

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

Return a shallow copy of the DoubleQueue.

def pushL(self, *ds: 'D') -> None:
303    def pushL(self, *ds: D) -> None:
304        """Push data onto left side (front) of `DoubleQueue`.
305
306        * like a Python List, does not return a value
307
308        """
309        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:
311    def pushR(self, *ds: D) -> None:
312        """Push data onto right side (rear) of `DoubleQueue`.
313
314        * like a Python List, does not return a value
315
316        """
317        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]':
319    def popL(self) -> MB[D]:
320        """Pop Data from left side (front) of `DoubleQueue`.
321
322        * pop left most item off of queue, return item in a maybe monad
323        * returns an empty `MB()` if queue is empty
324
325        """
326        if self._ca:
327            return MB(self._ca.popL())
328        else:
329            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]':
331    def popR(self) -> MB[D]:
332        """Pop Data from right side (rear) of `DoubleQueue`.
333
334        * pop right most item off of queue, return item in a maybe monad
335        * returns an empty `MB()` if queue is empty
336
337        """
338        if self._ca:
339            return MB(self._ca.popR())
340        else:
341            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]':
343    def peakL(self) -> MB[D]:
344        """Peak left side of `DoubleQueue`.
345
346        * return left most value in a maybe monad
347        * does not consume the item
348        * returns an empty `MB()` if queue is empty
349
350        """
351        if self._ca:
352            return MB(self._ca[0])
353        else:
354            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]':
356    def peakR(self) -> MB[D]:
357        """Peak right side of `DoubleQueue`.
358
359        * return right most value in a maybe monad
360        * does not consume the item
361        * returns an empty `MB()` if queue is empty
362
363        """
364        if self._ca:
365            return MB(self._ca[-1])
366        else:
367            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]':
369    def foldL[L](self, f: Callable[[L, D], L], initial: L|None=None, /) -> MB[L]:
370        """Fold `DoubleQueue` left to right.
371
372        Reduce left with `f` using an optional initial value.
373
374        * note that when an initial value is not given then `~L = ~D`
375        * if iterable empty & no initial value given, return `MB()`
376        * traditional FP type order given for function `f`
377
378        """
379        if initial is None:
380            if not self._ca:
381                return MB()
382        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]':
384    def foldR[R](self, f: Callable[[D, R], R], initial: R|None=None, /) -> MB[R]:
385        """Fold `DoubleQueue` right to left.
386
387        Reduce right with `f` using an optional initial value.
388
389        * note that when an initial value is not given then `~R = ~D`
390        * if iterable empty & no initial value given, return `MB()`
391        * traditional FP type order given for function `f`
392
393        """
394        if initial is None:
395            if not self._ca:
396                return MB()
397        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]':
399    def map[U](self, f: Callable[[D], U], /) -> DoubleQueue[U]:
400        """`Map a function over `DoubleQueue`.
401
402        * map the function `f` over the `DoubleQueue`
403          * left to right
404          * retain original order
405        * returns a new instance
406
407        """
408        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]):
 92class FIFOQueue[D](QueueBase[D]):
 93    """FIFO Queue
 94
 95    * stateful First-In-First-Out (FIFO) data structure
 96    * initial data pushed on in natural FIFO order
 97
 98    """
 99    __slots__ = ()
100
101    def __iter__(self) -> Iterator[D]:
102        return iter(list(self._ca))
103
104    def __repr__(self) -> str:
105        if len(self) == 0:
106            return 'FQ()'
107        else:
108            return 'FQ(' + ', '.join(map(repr, self._ca)) + ')'
109
110    def __str__(self) -> str:
111        return "<< " + " < ".join(map(str, self)) + " <<"
112
113    def copy(self) -> FIFOQueue[D]:
114        """Return a shallow copy of the `FIFOQueue`."""
115        return FIFOQueue(self._ca)
116
117    def push(self, *ds: D) -> None:
118        """Push data onto `FIFOQueue`.
119
120        * like a Python List, does not return a value
121
122        """
123        self._ca.pushR(*ds)
124
125    def pop(self) -> MB[D]:
126        """Pop data from `FIFOQueue`.
127
128        * pop item off queue, return item in a maybe monad
129        * returns an empty `MB()` if queue is empty
130
131        """
132        if self._ca:
133            return MB(self._ca.popL())
134        else:
135            return MB()
136
137    def peak_last_in(self) -> MB[D]:
138        """Peak last data into `FIFOQueue`.
139
140        * return a maybe monad of the last item pushed to queue
141        * does not consume the data
142        * if item already popped, return `MB()`
143
144        """
145        if self._ca:
146            return MB(self._ca[-1])
147        else:
148            return MB()
149
150    def peak_next_out(self) -> MB[D]:
151        """Peak next data out of `FIFOQueue`.
152
153        * returns a maybe monad of the next item to be popped from the queue.
154        * does not consume it the item
155        * returns `MB()` if queue is empty
156
157        """
158        if self._ca:
159            return MB(self._ca[0])
160        else:
161            return MB()
162
163    def fold[L](self, f: Callable[[L, D], L], initial: L|None=None, /) -> MB[L]:
164        """Fold `FIFOQueue` in natural order.
165
166        Reduce with `f` using an optional initial value.
167
168        * folds in natural FIFO Order (oldest to newest)
169        * note that when an initial value is not given then `~L = ~D`
170        * if iterable empty & no initial value given, return `MB()`
171        * traditional FP type order given for function `f`
172
173        """
174        if initial is None:
175            if not self._ca:
176                return MB()
177        return MB(self._ca.foldL(f, initial=initial))
178
179    def map[U](self, f: Callable[[D], U], /) -> FIFOQueue[U]:
180        """Map over the `FIFOQueue`.
181
182        * map function `f` over the queue
183          * oldest to newest
184          * retain original order
185        * returns a new instance
186
187        """
188        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]':
113    def copy(self) -> FIFOQueue[D]:
114        """Return a shallow copy of the `FIFOQueue`."""
115        return FIFOQueue(self._ca)

Return a shallow copy of the FIFOQueue.

def push(self, *ds: 'D') -> None:
117    def push(self, *ds: D) -> None:
118        """Push data onto `FIFOQueue`.
119
120        * like a Python List, does not return a value
121
122        """
123        self._ca.pushR(*ds)

Push data onto FIFOQueue.

  • like a Python List, does not return a value
def pop(self) -> 'MB[D]':
125    def pop(self) -> MB[D]:
126        """Pop data from `FIFOQueue`.
127
128        * pop item off queue, return item in a maybe monad
129        * returns an empty `MB()` if queue is empty
130
131        """
132        if self._ca:
133            return MB(self._ca.popL())
134        else:
135            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]':
137    def peak_last_in(self) -> MB[D]:
138        """Peak last data into `FIFOQueue`.
139
140        * return a maybe monad of the last item pushed to queue
141        * does not consume the data
142        * if item already popped, return `MB()`
143
144        """
145        if self._ca:
146            return MB(self._ca[-1])
147        else:
148            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]':
150    def peak_next_out(self) -> MB[D]:
151        """Peak next data out of `FIFOQueue`.
152
153        * returns a maybe monad of the next item to be popped from the queue.
154        * does not consume it the item
155        * returns `MB()` if queue is empty
156
157        """
158        if self._ca:
159            return MB(self._ca[0])
160        else:
161            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]':
163    def fold[L](self, f: Callable[[L, D], L], initial: L|None=None, /) -> MB[L]:
164        """Fold `FIFOQueue` in natural order.
165
166        Reduce with `f` using an optional initial value.
167
168        * folds in natural FIFO Order (oldest to newest)
169        * note that when an initial value is not given then `~L = ~D`
170        * if iterable empty & no initial value given, return `MB()`
171        * traditional FP type order given for function `f`
172
173        """
174        if initial is None:
175            if not self._ca:
176                return MB()
177        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]':
179    def map[U](self, f: Callable[[D], U], /) -> FIFOQueue[U]:
180        """Map over the `FIFOQueue`.
181
182        * map function `f` over the queue
183          * oldest to newest
184          * retain original order
185        * returns a new instance
186
187        """
188        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]):
190class LIFOQueue[D](QueueBase[D]):
191    """LIFO Queue.
192
193    * stateful Last-In-First-Out (LIFO) data structure
194    * initial data pushed on in natural LIFO order
195
196    """
197    __slots__ = ()
198
199    def __iter__(self) -> Iterator[D]:
200        return reversed(list(self._ca))
201
202    def __repr__(self) -> str:
203        if len(self) == 0:
204            return 'LQ()'
205        else:
206            return 'LQ(' + ', '.join(map(repr, self._ca)) + ')'
207
208    def __str__(self) -> str:
209        return "|| " + " > ".join(map(str, self)) + " ><"
210
211    def copy(self) -> LIFOQueue[D]:
212        """Return a shallow copy of the `LIFOQueue`."""
213        return LIFOQueue(reversed(self._ca))
214
215    def push(self, *ds: D) -> None:
216        """Push data onto `LIFOQueue`.
217
218        * like a Python List, does not return a value
219
220        """
221        self._ca.pushR(*ds)
222
223    def pop(self) -> MB[D]:
224        """Pop data from `LIFOQueue`.
225
226        * pop item off of queue, return item in a maybe monad
227        * returns an empty `MB()` if queue is empty
228
229        """
230        if self._ca:
231            return MB(self._ca.popR())
232        else:
233            return MB()
234
235    def peak(self) -> MB[D]:
236        """Peak next data out of `LIFOQueue`.
237
238        * return a maybe monad of the next item to be popped from the queue
239        * does not consume the item
240        * returns `MB()` if queue is empty
241
242        """
243        if self._ca:
244            return MB(self._ca[-1])
245        else:
246            return MB()
247
248    def fold[R](self, f: Callable[[D, R], R], initial: R|None=None, /) -> MB[R]:
249        """Fold `LIFOQueue` in natural order.
250
251        Reduce with `f` using an optional initial value.
252
253        * folds in natural LIFO Order (newest to oldest)
254        * note that when an initial value is not given then `~R = ~D`
255        * if iterable empty & no initial value given, return `MB()`
256        * traditional FP type order given for function `f`
257
258        """
259        if initial is None:
260            if not self._ca:
261                return MB()
262        return MB(self._ca.foldR(f, initial=initial))
263
264    def map[U](self, f: Callable[[D], U], /) -> LIFOQueue[U]:
265        """Map Over the `LIFOQueue`.
266
267        * map the function `f` over the queue
268          * newest to oldest
269          * retain original order
270        * returns a new instance
271
272        """
273        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]':
211    def copy(self) -> LIFOQueue[D]:
212        """Return a shallow copy of the `LIFOQueue`."""
213        return LIFOQueue(reversed(self._ca))

Return a shallow copy of the LIFOQueue.

def push(self, *ds: 'D') -> None:
215    def push(self, *ds: D) -> None:
216        """Push data onto `LIFOQueue`.
217
218        * like a Python List, does not return a value
219
220        """
221        self._ca.pushR(*ds)

Push data onto LIFOQueue.

  • like a Python List, does not return a value
def pop(self) -> 'MB[D]':
223    def pop(self) -> MB[D]:
224        """Pop data from `LIFOQueue`.
225
226        * pop item off of queue, return item in a maybe monad
227        * returns an empty `MB()` if queue is empty
228
229        """
230        if self._ca:
231            return MB(self._ca.popR())
232        else:
233            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]':
235    def peak(self) -> MB[D]:
236        """Peak next data out of `LIFOQueue`.
237
238        * return a maybe monad of the next item to be popped from the queue
239        * does not consume the item
240        * returns `MB()` if queue is empty
241
242        """
243        if self._ca:
244            return MB(self._ca[-1])
245        else:
246            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]':
248    def fold[R](self, f: Callable[[D, R], R], initial: R|None=None, /) -> MB[R]:
249        """Fold `LIFOQueue` in natural order.
250
251        Reduce with `f` using an optional initial value.
252
253        * folds in natural LIFO Order (newest to oldest)
254        * note that when an initial value is not given then `~R = ~D`
255        * if iterable empty & no initial value given, return `MB()`
256        * traditional FP type order given for function `f`
257
258        """
259        if initial is None:
260            if not self._ca:
261                return MB()
262        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]':
264    def map[U](self, f: Callable[[D], U], /) -> LIFOQueue[U]:
265        """Map Over the `LIFOQueue`.
266
267        * map the function `f` over the queue
268          * newest to oldest
269          * retain original order
270        * returns a new instance
271
272        """
273        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]):
53class QueueBase[D](Sequence[D]):
54    """Base class for circular area based queues.
55
56    * implemented with a grscheller.circular-array in a "has-a" relationship
57    * order of initial data retained
58    * slicing not yet implemented
59
60    """
61    __slots__ = '_ca'
62
63    def __init__(self, *dss: Iterable[D]) -> None:
64        if len(dss) < 2:
65            self._ca = ca(*dss)
66        else:
67            msg1 = f'{type(self).__name__}: expected at most 1 '
68            msg2 = f'iterable argument, got {len(dss)}.'
69            raise TypeError(msg1+msg2)
70
71    def __bool__(self) -> bool:
72        return len(self._ca) > 0
73
74    def __len__(self) -> int:
75        return len(self._ca)
76
77    def __eq__(self, other: object, /) -> bool:
78        if not isinstance(other, type(self)):
79            return False
80        return self._ca == other._ca
81
82    @overload
83    def __getitem__(self, idx: int, /) -> D: ...
84    @overload
85    def __getitem__(self, idx: slice, /) -> Sequence[D]: ...
86
87    def __getitem__(self, idx: int|slice, /) -> D|Sequence[D]|Never:
88        if isinstance(idx, slice):
89            raise NotImplementedError
90        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]')
63    def __init__(self, *dss: Iterable[D]) -> None:
64        if len(dss) < 2:
65            self._ca = ca(*dss)
66        else:
67            msg1 = f'{type(self).__name__}: expected at most 1 '
68            msg2 = f'iterable argument, got {len(dss)}.'
69            raise TypeError(msg1+msg2)
def DQ(*ds: 'D') -> 'DoubleQueue[D]':
418def DQ[D](*ds: D) -> DoubleQueue[D]:
419    """Return a DoubleQueue whose data is pushed on from the right."""
420    return DoubleQueue(ds)

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

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

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

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

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