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

Return a shallow copy of the DoubleQueue.

def pushL(self, *ds: 'D') -> None:
262    def pushL(self, *ds: D) -> None:
263        """Push data onto left side (front) of `DoubleQueue`.
264
265        * like a Python List, does not return a value
266
267        """
268        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:
270    def pushR(self, *ds: D) -> None:
271        """Push data onto right side (rear) of `DoubleQueue`.
272
273        * like a Python List, does not return a value
274
275        """
276        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]':
278    def popL(self) -> MB[D]:
279        """Pop Data from left side (front) of `DoubleQueue`.
280
281        * pop left most item off of queue, return item in a maybe monad
282        * returns an empty `MB()` if queue is empty
283
284        """
285        if self._ca:
286            return MB(self._ca.popL())
287        else:
288            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]':
290    def popR(self) -> MB[D]:
291        """Pop Data from right side (rear) of `DoubleQueue`.
292
293        * pop right most item off of queue, return item in a maybe monad
294        * returns an empty `MB()` if queue is empty
295
296        """
297        if self._ca:
298            return MB(self._ca.popR())
299        else:
300            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]':
302    def peakL(self) -> MB[D]:
303        """Peak left side of `DoubleQueue`.
304
305        * return left most value in a maybe monad
306        * does not consume the item
307        * returns an empty `MB()` if queue is empty
308
309        """
310        if self._ca:
311            return MB(self._ca[0])
312        else:
313            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]':
315    def peakR(self) -> MB[D]:
316        """Peak right side of `DoubleQueue`.
317
318        * return right most value in a maybe monad
319        * does not consume the item
320        * returns an empty `MB()` if queue is empty
321
322        """
323        if self._ca:
324            return MB(self._ca[-1])
325        else:
326            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: 'Optional[L]' = None) -> 'MB[L]':
328    def foldL[L](self, f: Callable[[L, D], L], initial: Optional[L]=None) -> MB[L]:
329        """Fold `DoubleQueue` left to right.
330
331        Reduce left with `f` using an optional initial value.
332
333        * note that when an initial value is not given then `~L = ~D`
334        * if iterable empty & no initial value given, return `MB()`
335        * traditional FP type order given for function `f`
336
337        """
338        if initial is None:
339            if not self._ca:
340                return MB()
341        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: 'Optional[R]' = None) -> 'MB[R]':
343    def foldR[R](self, f: Callable[[D, R], R], initial: Optional[R]=None) -> MB[R]:
344        """Fold `DoubleQueue` right to left.
345
346        Reduce right with `f` using an optional initial value.
347
348        * note that when an initial value is not given then `~R = ~D`
349        * if iterable empty & no initial value given, return `MB()`
350        * traditional FP type order given for function `f`
351
352        """
353        if initial is None:
354            if not self._ca:
355                return MB()
356        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]':
358    def map[U](self, f: Callable[[D], U]) -> DoubleQueue[U]:
359        """`Map a function over `DoubleQueue`.
360
361        * map the function `f` over the `DoubleQueue`
362          * left to right
363          * retain original order
364        * returns a new instance
365
366        """
367        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]):
 69class FIFOQueue[D](QueueBase[D]):
 70    """#### FIFO Queue
 71
 72    * stateful First-In-First-Out (FIFO) data structure
 73    * initial data pushed on in natural FIFO order
 74
 75    """
 76    __slots__ = ()
 77
 78    def __iter__(self) -> Iterator[D]:
 79        return iter(list(self._ca))
 80
 81    def copy(self) -> FIFOQueue[D]:
 82        """Return a shallow copy of the `FIFOQueue`."""
 83        return FIFOQueue(*self._ca)
 84
 85    def __str__(self) -> str:
 86        return "<< " + " < ".join(map(str, self)) + " <<"
 87
 88    def push(self, *ds: D) -> None:
 89        """Push data onto `FIFOQueue`.
 90
 91        * like a Python List, does not return a value
 92
 93        """
 94        self._ca.pushR(*ds)
 95
 96    def pop(self) -> MB[D]:
 97        """Pop data from `FIFOQueue`.
 98
 99        * pop item off queue, return item in a maybe monad
100        * returns an empty `MB()` if queue is empty
101
102        """
103        if self._ca:
104            return MB(self._ca.popL())
105        else:
106            return MB()
107
108    def peak_last_in(self) -> MB[D]:
109        """Peak last data into `FIFOQueue`.
110
111        * return a maybe monad of the last item pushed to queue
112        * does not consume the data
113        * if item already popped, return `MB()`
114
115        """
116        if self._ca:
117            return MB(self._ca[-1])
118        else:
119            return MB()
120
121    def peak_next_out(self) -> MB[D]:
122        """Peak next data out of `FIFOQueue`.
123
124        * returns a maybe monad of the next item to be popped from the queue.
125        * does not consume it the item
126        * returns `MB()` if queue is empty
127
128        """
129        if self._ca:
130            return MB(self._ca[0])
131        else:
132            return MB()
133
134    def fold[L](self, f: Callable[[L, D], L], initial: Optional[L]=None) -> MB[L]:
135        """Fold `FIFOQueue` in natural order.
136
137        Reduce with `f` using an optional initial value.
138
139        * folds in natural FIFO Order (oldest to newest)
140        * note that when an initial value is not given then `~L = ~D`
141        * if iterable empty & no initial value given, return `MB()`
142        * traditional FP type order given for function `f`
143
144        """
145        if initial is None:
146            if not self._ca:
147                return MB()
148        return MB(self._ca.foldL(f, initial=initial))
149
150    def map[U](self, f: Callable[[D], U]) -> FIFOQueue[U]:
151        """Map over the `FIFOQueue`.
152
153        * map function `f` over the queue
154          * oldest to newest
155          * retain original order
156        * returns a new instance
157
158        """
159        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]':
81    def copy(self) -> FIFOQueue[D]:
82        """Return a shallow copy of the `FIFOQueue`."""
83        return FIFOQueue(*self._ca)

Return a shallow copy of the FIFOQueue.

def push(self, *ds: 'D') -> None:
88    def push(self, *ds: D) -> None:
89        """Push data onto `FIFOQueue`.
90
91        * like a Python List, does not return a value
92
93        """
94        self._ca.pushR(*ds)

Push data onto FIFOQueue.

  • like a Python List, does not return a value
def pop(self) -> 'MB[D]':
 96    def pop(self) -> MB[D]:
 97        """Pop data from `FIFOQueue`.
 98
 99        * pop item off queue, return item in a maybe monad
100        * returns an empty `MB()` if queue is empty
101
102        """
103        if self._ca:
104            return MB(self._ca.popL())
105        else:
106            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]':
108    def peak_last_in(self) -> MB[D]:
109        """Peak last data into `FIFOQueue`.
110
111        * return a maybe monad of the last item pushed to queue
112        * does not consume the data
113        * if item already popped, return `MB()`
114
115        """
116        if self._ca:
117            return MB(self._ca[-1])
118        else:
119            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]':
121    def peak_next_out(self) -> MB[D]:
122        """Peak next data out of `FIFOQueue`.
123
124        * returns a maybe monad of the next item to be popped from the queue.
125        * does not consume it the item
126        * returns `MB()` if queue is empty
127
128        """
129        if self._ca:
130            return MB(self._ca[0])
131        else:
132            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: 'Optional[L]' = None) -> 'MB[L]':
134    def fold[L](self, f: Callable[[L, D], L], initial: Optional[L]=None) -> MB[L]:
135        """Fold `FIFOQueue` in natural order.
136
137        Reduce with `f` using an optional initial value.
138
139        * folds in natural FIFO Order (oldest to newest)
140        * note that when an initial value is not given then `~L = ~D`
141        * if iterable empty & no initial value given, return `MB()`
142        * traditional FP type order given for function `f`
143
144        """
145        if initial is None:
146            if not self._ca:
147                return MB()
148        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]':
150    def map[U](self, f: Callable[[D], U]) -> FIFOQueue[U]:
151        """Map over the `FIFOQueue`.
152
153        * map function `f` over the queue
154          * oldest to newest
155          * retain original order
156        * returns a new instance
157
158        """
159        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]):
161class LIFOQueue[D](QueueBase[D]):
162    """#### LIFO Queue
163
164    * stateful Last-In-First-Out (LIFO) data structure
165    * initial data pushed on in natural LIFO order
166
167    """
168    __slots__ = ()
169
170    def __iter__(self) -> Iterator[D]:
171        return reversed(list(self._ca))
172
173    def __str__(self) -> str:
174        return "|| " + " > ".join(map(str, self)) + " ><"
175
176    def copy(self) -> LIFOQueue[D]:
177        """Return a shallow copy of the `LIFOQueue`."""
178        return LIFOQueue(*reversed(self._ca))
179
180    def push(self, *ds: D) -> None:
181        """Push data onto `LIFOQueue`.
182
183        * like a Python List, does not return a value
184
185        """
186        self._ca.pushR(*ds)
187
188    def pop(self) -> MB[D]:
189        """Pop data from `LIFOQueue`.
190
191        * pop item off of queue, return item in a maybe monad
192        * returns an empty `MB()` if queue is empty
193
194        """
195        if self._ca:
196            return MB(self._ca.popR())
197        else:
198            return MB()
199
200    def peak(self) -> MB[D]:
201        """Peak next data out of `LIFOQueue`.
202
203        * return a maybe monad of the next item to be popped from the queue
204        * does not consume the item
205        * returns `MB()` if queue is empty
206        
207        """
208        if self._ca:
209            return MB(self._ca[-1])
210        else:
211            return MB()
212
213    def fold[R](self, f: Callable[[D, R], R], initial: Optional[R]=None) -> MB[R]:
214        """Fold `LIFOQueue` in natural order.
215
216        Reduce with `f` using an optional initial value.
217
218        * folds in natural LIFO Order (newest to oldest)
219        * note that when an initial value is not given then `~R = ~D`
220        * if iterable empty & no initial value given, return `MB()`
221        * traditional FP type order given for function `f`
222
223        """
224        if initial is None:
225            if not self._ca:
226                return MB()
227        return MB(self._ca.foldR(f, initial=initial))
228
229    def map[U](self, f: Callable[[D], U]) -> LIFOQueue[U]:
230        """Map Over the `LIFOQueue`.
231
232        * map the function `f` over the queue
233          * newest to oldest
234          * retain original order
235        * returns a new instance
236
237        """
238        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]':
176    def copy(self) -> LIFOQueue[D]:
177        """Return a shallow copy of the `LIFOQueue`."""
178        return LIFOQueue(*reversed(self._ca))

Return a shallow copy of the LIFOQueue.

def push(self, *ds: 'D') -> None:
180    def push(self, *ds: D) -> None:
181        """Push data onto `LIFOQueue`.
182
183        * like a Python List, does not return a value
184
185        """
186        self._ca.pushR(*ds)

Push data onto LIFOQueue.

  • like a Python List, does not return a value
def pop(self) -> 'MB[D]':
188    def pop(self) -> MB[D]:
189        """Pop data from `LIFOQueue`.
190
191        * pop item off of queue, return item in a maybe monad
192        * returns an empty `MB()` if queue is empty
193
194        """
195        if self._ca:
196            return MB(self._ca.popR())
197        else:
198            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]':
200    def peak(self) -> MB[D]:
201        """Peak next data out of `LIFOQueue`.
202
203        * return a maybe monad of the next item to be popped from the queue
204        * does not consume the item
205        * returns `MB()` if queue is empty
206        
207        """
208        if self._ca:
209            return MB(self._ca[-1])
210        else:
211            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: 'Optional[R]' = None) -> 'MB[R]':
213    def fold[R](self, f: Callable[[D, R], R], initial: Optional[R]=None) -> MB[R]:
214        """Fold `LIFOQueue` in natural order.
215
216        Reduce with `f` using an optional initial value.
217
218        * folds in natural LIFO Order (newest to oldest)
219        * note that when an initial value is not given then `~R = ~D`
220        * if iterable empty & no initial value given, return `MB()`
221        * traditional FP type order given for function `f`
222
223        """
224        if initial is None:
225            if not self._ca:
226                return MB()
227        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]':
229    def map[U](self, f: Callable[[D], U]) -> LIFOQueue[U]:
230        """Map Over the `LIFOQueue`.
231
232        * map the function `f` over the queue
233          * newest to oldest
234          * retain original order
235        * returns a new instance
236
237        """
238        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(typing.Generic[D]):
39class QueueBase[D]():
40    """#### Base class for circular area based queues.
41
42    * primarily for DRY inheritance
43    * implemented with a grscheller.circular-array (has-a)
44    * order of initial data retained
45
46    """
47    __slots__ = '_ca'
48
49    def __init__(self, *ds: D):
50        self._ca = CA(*ds)
51
52    def __repr__(self) -> str:
53        if len(self) == 0:
54            return type(self).__name__ + '()'
55        else:
56            return type(self).__name__ + '(' + ', '.join(map(repr, self._ca)) + ')'
57
58    def __bool__(self) -> bool:
59        return len(self._ca) > 0
60
61    def __len__(self) -> int:
62        return len(self._ca)
63
64    def __eq__(self, other: object) -> bool:
65        if not isinstance(other, type(self)):
66            return False
67        return self._ca == other._ca

Base class for circular area based queues.

  • primarily for DRY inheritance
  • implemented with a grscheller.circular-array (has-a)
  • order of initial data retained
QueueBase(*ds: 'D')
49    def __init__(self, *ds: D):
50        self._ca = CA(*ds)