Module grscheller.datastructures.queues

Module grscheller.datastructure.queues - queue based datastructures

Module implementing stateful queue data structures with amortized O(1) pushing and popping from the queue. Obtaining length (number of elements) of a queue is an O(1) operation. Implemented with a Python List based circular array, these data structures will resize themselves as needed. Does not store None as a value.

Expand source code
# Copyright 2023-2024 Geoffrey R. Scheller
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Module grscheller.datastructure.queues - queue based datastructures

Module implementing stateful queue data structures with amortized O(1) pushing and
popping from the queue. Obtaining length (number of elements) of a queue is an O(1)
operation. Implemented with a Python List based circular array, these data structures
will resize themselves as needed. Does not store None as a value.
"""

from __future__ import annotations

__all__ = ['DoubleQueue', 'FIFOQueue', 'LIFOQueue']
__author__ = "Geoffrey R. Scheller"
__copyright__ = "Copyright (c) 2023-2024 Geoffrey R. Scheller"
__license__ = "Apache License 2.0"

from typing import Any, Callable
from .core.fp import FP
from grscheller.circular_array import CircularArray

class QueueBase():
    """Abstract base class for the purposes of DRY inheritance of classes
    implementing queue type data structures with a list based circular array.
    Each queue object "has-a" (contains) a circular array to store its data. The
    circular array used will resize itself as needed. Each QueueBase subclass
    must ensure that None values do not get pushed onto the circular array.
    """
    __slots__ = '_ca',

    def __init__(self, *ds):
        """Construct a queue data structure. Cull None values."""
        self._ca = CircularArray()
        for d in ds:
            if d is not None:
                self._ca.pushR(d)

    def __iter__(self):
        """Iterator yielding data currently stored in queue. Data yielded in
        natural FIFO order.
        """
        cached = self._ca.copy()
        for pos in range(len(cached)):
            yield cached[pos]

    def __reversed__(self):
        """Reverse iterate over the current state of the queue."""
        cached = self._ca.copy()
        for pos in range(len(cached)-1, -1, -1):
            yield cached[pos]

    def __repr__(self):
        return f'{self.__class__.__name__}(' + ', '.join(map(repr, self)) + ')'

    def __bool__(self):
        """Returns true if queue is not empty."""
        return len(self._ca) > 0

    def __len__(self):
        """Returns current number of values in queue."""
        return len(self._ca)

    def __eq__(self, other):
        """Returns True if all the data stored in both compare as equal.
        Worst case is O(n) behavior for the true case.
        """
        if not isinstance(other, type(self)):
            return False
        return self._ca == other._ca

    def map(self, f: Callable[[Any], Any]) -> None:
        """Apply function over the queue's contents. Suppress any None values
        returned by f.
        """
        self._ca = QueueBase(*map(f, self))._ca

    def reverse(self):
        """Reverse the elements in the Queue"""
        self._ca = self._ca.reverse()

class FIFOQueue(QueueBase, FP):
    """Stateful single sided FIFO data structure. Will resize itself as needed.
    None represents the absence of a value and ignored if pushed onto an FIFOQueue.
    """
    __slots__ = ()

    def __str__(self):
        return "<< " + " < ".join(map(str, self)) + " <<"

    def copy(self) -> FIFOQueue:
        """Return shallow copy of the FIFOQueue in O(n) time & space complexity."""
        fifoqueue = FIFOQueue()
        fifoqueue._ca = self._ca.copy()
        return fifoqueue

    def push(self, *ds: Any) -> None:
        """Push data on rear of the FIFOQueue & no return value."""
        for d in ds:
            if d != None:
                self._ca.pushR(d)

    def pop(self) -> Any:
        """Pop data off front of the FIFOQueue."""
        return self._ca.popL()

    def peakLastIn(self) -> Any:
        """Return last element pushed to the FIFOQueue without consuming it"""
        if self._ca:
            return self._ca[-1]
        else:
            return None

    def peakNextOut(self) -> Any:
        """Return next element ready to pop from the FIFOQueue."""
        if self._ca:
            return self._ca[0]
        else:
            return None

class LIFOQueue(QueueBase, FP):
    """Stateful single sided LIFO data structure. Will resize itself as needed.
    None represents the absence of a value and ignored if pushed onto an FIFOQueue.
    """
    __slots__ = ()

    def __str__(self):
        return "|| " + " > ".join(map(str, self)) + " ><"

    def copy(self) -> LIFOQueue:
        """Return shallow copy of the FIFOQueue in O(n) time & space complexity."""
        lifoqueue = LIFOQueue()
        lifoqueue._ca = self._ca.copy()
        return lifoqueue

    def push(self, *ds: Any) -> None:
        """Push data on rear of the LIFOQueue & no return value."""
        for d in ds:
            if d != None:
                self._ca.pushR(d)

    def pop(self) -> Any:
        """Pop data off rear of the LIFOQueue."""
        return self._ca.popR()

    def peak(self) -> Any:
        """Return last element pushed to the LIFOQueue without consuming it"""
        if self._ca:
            return self._ca[-1]
        else:
            return None

class DoubleQueue(QueueBase, FP):
    """Stateful double sided queue datastructure. Will resize itself as needed.
    None represents the absence of a value and ignored if pushed onto a DoubleQueue.
    """
    __slots__ = ()

    def __str__(self):
        return ">< " + " | ".join(map(str, self)) + " ><"

    def copy(self) -> DoubleQueue:
        """Return shallow copy of the DoubleQueue in O(n) time & space complexity."""
        dqueue = DoubleQueue()
        dqueue._ca = self._ca.copy()
        return dqueue

    def pushR(self, *ds: Any) -> None:
        """Push data left to right onto rear of the DoubleQueue."""
        for d in ds:
            if d != None:
                self._ca.pushR(d)

    def pushL(self, *ds: Any) -> None:
        """Push data left to right onto front of DoubleQueue."""
        for d in ds:
            if d != None:
                self._ca.pushL(d)

    def popR(self) -> Any:
        """Pop data off rear of the DoubleQueue"""
        return self._ca.popR()

    def popL(self) -> Any:
        """Pop data off front of the DoubleQueue"""
        return self._ca.popL()

    def peakR(self) -> Any:
        """Return right-most element of the DoubleQueue if it exists."""
        if self._ca:
            return self._ca[-1]
        else:
            return None

    def peakL(self) -> Any:
        """Return left-most element of the DoubleQueue if it exists."""
        if self._ca:
            return self._ca[0]
        else:
            return None

    def __getitem__(self, index: int) -> Any:
        return self._ca[index]

    def __setitem__(self, index: int, value):
        self._ca[index] = value

if __name__ == "__main__":
    pass

Classes

class DoubleQueue (*ds)

Stateful double sided queue datastructure. Will resize itself as needed. None represents the absence of a value and ignored if pushed onto a DoubleQueue.

Construct a queue data structure. Cull None values.

Expand source code
class DoubleQueue(QueueBase, FP):
    """Stateful double sided queue datastructure. Will resize itself as needed.
    None represents the absence of a value and ignored if pushed onto a DoubleQueue.
    """
    __slots__ = ()

    def __str__(self):
        return ">< " + " | ".join(map(str, self)) + " ><"

    def copy(self) -> DoubleQueue:
        """Return shallow copy of the DoubleQueue in O(n) time & space complexity."""
        dqueue = DoubleQueue()
        dqueue._ca = self._ca.copy()
        return dqueue

    def pushR(self, *ds: Any) -> None:
        """Push data left to right onto rear of the DoubleQueue."""
        for d in ds:
            if d != None:
                self._ca.pushR(d)

    def pushL(self, *ds: Any) -> None:
        """Push data left to right onto front of DoubleQueue."""
        for d in ds:
            if d != None:
                self._ca.pushL(d)

    def popR(self) -> Any:
        """Pop data off rear of the DoubleQueue"""
        return self._ca.popR()

    def popL(self) -> Any:
        """Pop data off front of the DoubleQueue"""
        return self._ca.popL()

    def peakR(self) -> Any:
        """Return right-most element of the DoubleQueue if it exists."""
        if self._ca:
            return self._ca[-1]
        else:
            return None

    def peakL(self) -> Any:
        """Return left-most element of the DoubleQueue if it exists."""
        if self._ca:
            return self._ca[0]
        else:
            return None

    def __getitem__(self, index: int) -> Any:
        return self._ca[index]

    def __setitem__(self, index: int, value):
        self._ca[index] = value

Ancestors

  • grscheller.datastructures.queues.QueueBase
  • FP

Methods

def copy(self) ‑> DoubleQueue

Return shallow copy of the DoubleQueue in O(n) time & space complexity.

Expand source code
def copy(self) -> DoubleQueue:
    """Return shallow copy of the DoubleQueue in O(n) time & space complexity."""
    dqueue = DoubleQueue()
    dqueue._ca = self._ca.copy()
    return dqueue
def peakL(self) ‑> Any

Return left-most element of the DoubleQueue if it exists.

Expand source code
def peakL(self) -> Any:
    """Return left-most element of the DoubleQueue if it exists."""
    if self._ca:
        return self._ca[0]
    else:
        return None
def peakR(self) ‑> Any

Return right-most element of the DoubleQueue if it exists.

Expand source code
def peakR(self) -> Any:
    """Return right-most element of the DoubleQueue if it exists."""
    if self._ca:
        return self._ca[-1]
    else:
        return None
def popL(self) ‑> Any

Pop data off front of the DoubleQueue

Expand source code
def popL(self) -> Any:
    """Pop data off front of the DoubleQueue"""
    return self._ca.popL()
def popR(self) ‑> Any

Pop data off rear of the DoubleQueue

Expand source code
def popR(self) -> Any:
    """Pop data off rear of the DoubleQueue"""
    return self._ca.popR()
def pushL(self, *ds: Any) ‑> None

Push data left to right onto front of DoubleQueue.

Expand source code
def pushL(self, *ds: Any) -> None:
    """Push data left to right onto front of DoubleQueue."""
    for d in ds:
        if d != None:
            self._ca.pushL(d)
def pushR(self, *ds: Any) ‑> None

Push data left to right onto rear of the DoubleQueue.

Expand source code
def pushR(self, *ds: Any) -> None:
    """Push data left to right onto rear of the DoubleQueue."""
    for d in ds:
        if d != None:
            self._ca.pushR(d)

Inherited members

class FIFOQueue (*ds)

Stateful single sided FIFO data structure. Will resize itself as needed. None represents the absence of a value and ignored if pushed onto an FIFOQueue.

Construct a queue data structure. Cull None values.

Expand source code
class FIFOQueue(QueueBase, FP):
    """Stateful single sided FIFO data structure. Will resize itself as needed.
    None represents the absence of a value and ignored if pushed onto an FIFOQueue.
    """
    __slots__ = ()

    def __str__(self):
        return "<< " + " < ".join(map(str, self)) + " <<"

    def copy(self) -> FIFOQueue:
        """Return shallow copy of the FIFOQueue in O(n) time & space complexity."""
        fifoqueue = FIFOQueue()
        fifoqueue._ca = self._ca.copy()
        return fifoqueue

    def push(self, *ds: Any) -> None:
        """Push data on rear of the FIFOQueue & no return value."""
        for d in ds:
            if d != None:
                self._ca.pushR(d)

    def pop(self) -> Any:
        """Pop data off front of the FIFOQueue."""
        return self._ca.popL()

    def peakLastIn(self) -> Any:
        """Return last element pushed to the FIFOQueue without consuming it"""
        if self._ca:
            return self._ca[-1]
        else:
            return None

    def peakNextOut(self) -> Any:
        """Return next element ready to pop from the FIFOQueue."""
        if self._ca:
            return self._ca[0]
        else:
            return None

Ancestors

  • grscheller.datastructures.queues.QueueBase
  • FP

Methods

def copy(self) ‑> FIFOQueue

Return shallow copy of the FIFOQueue in O(n) time & space complexity.

Expand source code
def copy(self) -> FIFOQueue:
    """Return shallow copy of the FIFOQueue in O(n) time & space complexity."""
    fifoqueue = FIFOQueue()
    fifoqueue._ca = self._ca.copy()
    return fifoqueue
def peakLastIn(self) ‑> Any

Return last element pushed to the FIFOQueue without consuming it

Expand source code
def peakLastIn(self) -> Any:
    """Return last element pushed to the FIFOQueue without consuming it"""
    if self._ca:
        return self._ca[-1]
    else:
        return None
def peakNextOut(self) ‑> Any

Return next element ready to pop from the FIFOQueue.

Expand source code
def peakNextOut(self) -> Any:
    """Return next element ready to pop from the FIFOQueue."""
    if self._ca:
        return self._ca[0]
    else:
        return None
def pop(self) ‑> Any

Pop data off front of the FIFOQueue.

Expand source code
def pop(self) -> Any:
    """Pop data off front of the FIFOQueue."""
    return self._ca.popL()
def push(self, *ds: Any) ‑> None

Push data on rear of the FIFOQueue & no return value.

Expand source code
def push(self, *ds: Any) -> None:
    """Push data on rear of the FIFOQueue & no return value."""
    for d in ds:
        if d != None:
            self._ca.pushR(d)

Inherited members

class LIFOQueue (*ds)

Stateful single sided LIFO data structure. Will resize itself as needed. None represents the absence of a value and ignored if pushed onto an FIFOQueue.

Construct a queue data structure. Cull None values.

Expand source code
class LIFOQueue(QueueBase, FP):
    """Stateful single sided LIFO data structure. Will resize itself as needed.
    None represents the absence of a value and ignored if pushed onto an FIFOQueue.
    """
    __slots__ = ()

    def __str__(self):
        return "|| " + " > ".join(map(str, self)) + " ><"

    def copy(self) -> LIFOQueue:
        """Return shallow copy of the FIFOQueue in O(n) time & space complexity."""
        lifoqueue = LIFOQueue()
        lifoqueue._ca = self._ca.copy()
        return lifoqueue

    def push(self, *ds: Any) -> None:
        """Push data on rear of the LIFOQueue & no return value."""
        for d in ds:
            if d != None:
                self._ca.pushR(d)

    def pop(self) -> Any:
        """Pop data off rear of the LIFOQueue."""
        return self._ca.popR()

    def peak(self) -> Any:
        """Return last element pushed to the LIFOQueue without consuming it"""
        if self._ca:
            return self._ca[-1]
        else:
            return None

Ancestors

  • grscheller.datastructures.queues.QueueBase
  • FP

Methods

def copy(self) ‑> LIFOQueue

Return shallow copy of the FIFOQueue in O(n) time & space complexity.

Expand source code
def copy(self) -> LIFOQueue:
    """Return shallow copy of the FIFOQueue in O(n) time & space complexity."""
    lifoqueue = LIFOQueue()
    lifoqueue._ca = self._ca.copy()
    return lifoqueue
def peak(self) ‑> Any

Return last element pushed to the LIFOQueue without consuming it

Expand source code
def peak(self) -> Any:
    """Return last element pushed to the LIFOQueue without consuming it"""
    if self._ca:
        return self._ca[-1]
    else:
        return None
def pop(self) ‑> Any

Pop data off rear of the LIFOQueue.

Expand source code
def pop(self) -> Any:
    """Pop data off rear of the LIFOQueue."""
    return self._ca.popR()
def push(self, *ds: Any) ‑> None

Push data on rear of the LIFOQueue & no return value.

Expand source code
def push(self, *ds: Any) -> None:
    """Push data on rear of the LIFOQueue & no return value."""
    for d in ds:
        if d != None:
            self._ca.pushR(d)

Inherited members