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