Module grscheller.datastructures.dqueue

Module grscheller.datastructure.dqueue - Double sided queue

Double sided queue with amortized O(1) insertions & deletions from either end. Obtaining length (number of elements) of a Dqueue is also a O(1) operation.

Implemented with a Python List based circular array.

Expand source code
# Copyright 2023 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.dqueue - Double sided queue

Double sided queue with amortized O(1) insertions & deletions from either end.
Obtaining length (number of elements) of a Dqueue is also a O(1) operation.

Implemented with a Python List based circular array.
"""

from __future__ import annotations

__all__ = ['Dqueue']
__author__ = "Geoffrey R. Scheller"
__copyright__ = "Copyright (c) 2023 Geoffrey R. Scheller"
__license__ = "Appache License 2.0"

from typing import Any, Callable
from .circle import Circle
from .functional.maybe import Maybe, Nothing, Some
from .iterlib import concatIters, mapIter

class Dqueue():
    """Double sided queue datastructure. Will resize itself as needed.

    Does not throw exceptions. The Dqueue class consistently uses None to
    represent the absence of a value. Therefore some care needs to be taken
    when Python None is pushed onto Dqueue objects.
    """
    def __init__(self, *ds):
        """Construct a double sided queue"""
        self._circle = Circle()
        for d in ds:
            self._circle.pushR(d)

    def __bool__(self) -> bool:
        """Returns true if dqueue is not empty"""
        return len(self._circle) != 0

    def __len__(self) -> int:
        """Returns current number of values in dqueue"""
        return len(self._circle)

    def __iter__(self):
        """Iterator yielding data currently stored in dqueue"""
        currCircle = self._circle.copy()
        for pos in range(len(currCircle)):
            yield currCircle[pos]

    def __reversed__(self):
        """Reverse iterate over the current state of the dqueue"""
        for data in reversed(self._circle.copy()):
            yield data

    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._circle == other._circle

    def __repr__(self):
        """Display data in dqueue"""
        dataListStrs = []
        for data in self._circle:
            dataListStrs.append(repr(data))
        return ">< " + " | ".join(dataListStrs) + " ><"

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

    def pushR(self, *ds: Any) -> Dqueue:
        """Push data on rear of dqueue & return reference to self"""
        for d in ds:
            if d != None:
                self._circle.pushR(d)
        return self

    def pushL(self, *ds: Any) -> Dqueue:
        """Push data on front of dqueue, return reference to self"""
        for d in ds:
            if d != None:
                self._circle.pushL(d)
        return self

    def popR(self) -> Maybe:
        """Pop data off rear of dqueue"""
        if len(self._circle) > 0:
            return Some(self._circle.popR())
        else:
            return Nothing

    def popL(self) -> Maybe:
        """Pop data off front of dqueue"""
        if len(self._circle) > 0:
            return Some(self._circle.popL())
        else:
            return Nothing

    def headR(self) -> Maybe:
        """Return rear element of dqueue without consuming it"""
        if len(self._circle) > 0:
            return Some(self._circle[-1])
        else:
            return Nothing

    def headL(self) -> Maybe:
        """Return front element of dqueue without consuming it"""
        if len(self._circle) > 0:
            return Some(self._circle[0])
        else:
            return Nothing

    def capacity(self) -> int:
        """Returns current capacity of dqueue"""
        return self._circle.capacity()

    def fractionFilled(self) -> float:
        """Returns current capacity of dqueue"""
        return self._circle.fractionFilled()

    def resize(self, addCapacity = 0):
        """Compact dqueue and add extra capacity"""
        return self._circle.resize(addCapacity)

    def map(self, f: Callable[[Any], Any]) -> Dqueue:
        """Apply function over dqueue contents, returns new instance"""
        return Dqueue(*mapIter(iter(self), f))

    def mapSelf(self, f: Callable[[Any], Any]) -> Dqueue:
        """Apply function over dqueue contents"""
        copy = Dqueue(*mapIter(iter(self), f))
        self._circle = copy._circle
        return self

    def flatMap(self, f: Callable[[Any], Dqueue]) -> Dqueue:
        """Apply function and flatten result, returns new instance"""
        return Dqueue(
            *concatIters(
                *mapIter(mapIter(iter(self), f), lambda x: iter(x))
            )
        )

if __name__ == "__main__":
    pass

Classes

class Dqueue (*ds)

Double sided queue datastructure. Will resize itself as needed.

Does not throw exceptions. The Dqueue class consistently uses None to represent the absence of a value. Therefore some care needs to be taken when Python None is pushed onto Dqueue objects.

Construct a double sided queue

Expand source code
class Dqueue():
    """Double sided queue datastructure. Will resize itself as needed.

    Does not throw exceptions. The Dqueue class consistently uses None to
    represent the absence of a value. Therefore some care needs to be taken
    when Python None is pushed onto Dqueue objects.
    """
    def __init__(self, *ds):
        """Construct a double sided queue"""
        self._circle = Circle()
        for d in ds:
            self._circle.pushR(d)

    def __bool__(self) -> bool:
        """Returns true if dqueue is not empty"""
        return len(self._circle) != 0

    def __len__(self) -> int:
        """Returns current number of values in dqueue"""
        return len(self._circle)

    def __iter__(self):
        """Iterator yielding data currently stored in dqueue"""
        currCircle = self._circle.copy()
        for pos in range(len(currCircle)):
            yield currCircle[pos]

    def __reversed__(self):
        """Reverse iterate over the current state of the dqueue"""
        for data in reversed(self._circle.copy()):
            yield data

    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._circle == other._circle

    def __repr__(self):
        """Display data in dqueue"""
        dataListStrs = []
        for data in self._circle:
            dataListStrs.append(repr(data))
        return ">< " + " | ".join(dataListStrs) + " ><"

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

    def pushR(self, *ds: Any) -> Dqueue:
        """Push data on rear of dqueue & return reference to self"""
        for d in ds:
            if d != None:
                self._circle.pushR(d)
        return self

    def pushL(self, *ds: Any) -> Dqueue:
        """Push data on front of dqueue, return reference to self"""
        for d in ds:
            if d != None:
                self._circle.pushL(d)
        return self

    def popR(self) -> Maybe:
        """Pop data off rear of dqueue"""
        if len(self._circle) > 0:
            return Some(self._circle.popR())
        else:
            return Nothing

    def popL(self) -> Maybe:
        """Pop data off front of dqueue"""
        if len(self._circle) > 0:
            return Some(self._circle.popL())
        else:
            return Nothing

    def headR(self) -> Maybe:
        """Return rear element of dqueue without consuming it"""
        if len(self._circle) > 0:
            return Some(self._circle[-1])
        else:
            return Nothing

    def headL(self) -> Maybe:
        """Return front element of dqueue without consuming it"""
        if len(self._circle) > 0:
            return Some(self._circle[0])
        else:
            return Nothing

    def capacity(self) -> int:
        """Returns current capacity of dqueue"""
        return self._circle.capacity()

    def fractionFilled(self) -> float:
        """Returns current capacity of dqueue"""
        return self._circle.fractionFilled()

    def resize(self, addCapacity = 0):
        """Compact dqueue and add extra capacity"""
        return self._circle.resize(addCapacity)

    def map(self, f: Callable[[Any], Any]) -> Dqueue:
        """Apply function over dqueue contents, returns new instance"""
        return Dqueue(*mapIter(iter(self), f))

    def mapSelf(self, f: Callable[[Any], Any]) -> Dqueue:
        """Apply function over dqueue contents"""
        copy = Dqueue(*mapIter(iter(self), f))
        self._circle = copy._circle
        return self

    def flatMap(self, f: Callable[[Any], Dqueue]) -> Dqueue:
        """Apply function and flatten result, returns new instance"""
        return Dqueue(
            *concatIters(
                *mapIter(mapIter(iter(self), f), lambda x: iter(x))
            )
        )

Methods

def capacity(self) ‑> int

Returns current capacity of dqueue

Expand source code
def capacity(self) -> int:
    """Returns current capacity of dqueue"""
    return self._circle.capacity()
def copy(self) ‑> Dqueue

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

Expand source code
def copy(self) -> Dqueue:
    """Return shallow copy of the dqueue in O(n) time & space complexity"""
    new_dqueue = Dqueue()
    new_dqueue._circle = self._circle.copy()
    return new_dqueue
def flatMap(self, f: Callable[[Any], Dqueue]) ‑> Dqueue

Apply function and flatten result, returns new instance

Expand source code
def flatMap(self, f: Callable[[Any], Dqueue]) -> Dqueue:
    """Apply function and flatten result, returns new instance"""
    return Dqueue(
        *concatIters(
            *mapIter(mapIter(iter(self), f), lambda x: iter(x))
        )
    )
def fractionFilled(self) ‑> float

Returns current capacity of dqueue

Expand source code
def fractionFilled(self) -> float:
    """Returns current capacity of dqueue"""
    return self._circle.fractionFilled()
def headL(self) ‑> Maybe

Return front element of dqueue without consuming it

Expand source code
def headL(self) -> Maybe:
    """Return front element of dqueue without consuming it"""
    if len(self._circle) > 0:
        return Some(self._circle[0])
    else:
        return Nothing
def headR(self) ‑> Maybe

Return rear element of dqueue without consuming it

Expand source code
def headR(self) -> Maybe:
    """Return rear element of dqueue without consuming it"""
    if len(self._circle) > 0:
        return Some(self._circle[-1])
    else:
        return Nothing
def map(self, f: Callable[[Any], Any]) ‑> Dqueue

Apply function over dqueue contents, returns new instance

Expand source code
def map(self, f: Callable[[Any], Any]) -> Dqueue:
    """Apply function over dqueue contents, returns new instance"""
    return Dqueue(*mapIter(iter(self), f))
def mapSelf(self, f: Callable[[Any], Any]) ‑> Dqueue

Apply function over dqueue contents

Expand source code
def mapSelf(self, f: Callable[[Any], Any]) -> Dqueue:
    """Apply function over dqueue contents"""
    copy = Dqueue(*mapIter(iter(self), f))
    self._circle = copy._circle
    return self
def popL(self) ‑> Maybe

Pop data off front of dqueue

Expand source code
def popL(self) -> Maybe:
    """Pop data off front of dqueue"""
    if len(self._circle) > 0:
        return Some(self._circle.popL())
    else:
        return Nothing
def popR(self) ‑> Maybe

Pop data off rear of dqueue

Expand source code
def popR(self) -> Maybe:
    """Pop data off rear of dqueue"""
    if len(self._circle) > 0:
        return Some(self._circle.popR())
    else:
        return Nothing
def pushL(self, *ds: Any) ‑> Dqueue

Push data on front of dqueue, return reference to self

Expand source code
def pushL(self, *ds: Any) -> Dqueue:
    """Push data on front of dqueue, return reference to self"""
    for d in ds:
        if d != None:
            self._circle.pushL(d)
    return self
def pushR(self, *ds: Any) ‑> Dqueue

Push data on rear of dqueue & return reference to self

Expand source code
def pushR(self, *ds: Any) -> Dqueue:
    """Push data on rear of dqueue & return reference to self"""
    for d in ds:
        if d != None:
            self._circle.pushR(d)
    return self
def resize(self, addCapacity=0)

Compact dqueue and add extra capacity

Expand source code
def resize(self, addCapacity = 0):
    """Compact dqueue and add extra capacity"""
    return self._circle.resize(addCapacity)