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)
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
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
.
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
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
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
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
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
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
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
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
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 over
DoubleQueue`.
- map the function
f
over theDoubleQueue
- left to right
- retain original order
- returns a new instance
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
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
.
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
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
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()
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
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
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
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
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
.
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
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
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
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
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
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
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.
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.
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.