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