Module grscheller.datastructures.tuples

Immutable Tuple-like data structure with a functional interfaces.

Types of Tuples:

  • class FTuple: extend builtin tuple with functional interface

Classes

class FTuple (*ds: _T)

Class implementing a Tuple-like object with FP behaviors.

Expand source code
class FTuple(Generic[_T]):
    """Class implementing a Tuple-like object with FP behaviors."""
    __slots__ = '_tuple'

    def __init__(self, *ds: _T):
        self._tuple = tuple(ds)

    def __iter__(self) -> Iterator[_T]:
        return iter(self._tuple)

    def __reversed__(self) -> Iterator[_T]:
        return reversed(self._tuple)

    def __bool__(self) -> bool:
        return bool(len(self._tuple))

    def __len__(self) -> int:
        return len(self._tuple)

    def __repr__(self) -> str:
        return 'FTuple(' + ', '.join(map(repr, self)) + ')'

    def __str__(self) -> str:
        return "((" + ", ".join(map(repr, self)) + "))"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, type(self)):
            return False
        return self._tuple == other._tuple

    def __getitem__(self, sl: slice|int) -> FTuple[_T]|Optional[_T]:
        """Supports both indexing and slicing."""
        if isinstance(sl, slice):
            tup = self._tuple[sl]
            return FTuple(*tup)
        try:
            item = self._tuple[sl]
        except IndexError:
            item = None
        return item

    def foldL(self, f: Callable[[_T, _T], _T]) -> Optional[_T]:
        """Fold (reduce) left.

        * first argument of `f` is for the accumulated value
        * if FTuple is empty, return `None`

        """
        if len(self._tuple) == 0:
            return None

        iter_self = iter(self)
        acc = next(iter_self)

        for v in iter_self:
            acc = f(acc, v)

        return acc

    def foldR(self, f: Callable[[_T, _T], _T]) -> Optional[_T]:
        """Fold (reduce) right.

        * second argument of `f` is for the accumulated value
        * if FTuple is empty, return `None`

        """
        if len(self._tuple) == 0:
            return None

        rev_self = reversed(self)
        acc = next(rev_self)
        for v in rev_self:
            acc = f(v, acc)

        return acc

    def foldL1(self, f: Callable[[_S, _T], _S], s: _S) -> _S:
        """Fold left with an initial value.

        * first argument of `f` is for the accumulated value
        * if empty, return the initial value s

        """
        acc = s
        for v in self:
            acc = f(acc, v)
        return acc

    def foldR1(self, f: Callable[[_T, _S], _S], s: _S) -> _S:
        """Fold right with an initial value.

        * second argument of f is for the accumulated value
        * if empty, return the initial value s

        """
        acc = s
        for v in reversed(self):
            acc = f(v, acc)
        return acc

    def copy(self) -> FTuple[_T]:
        """Return shallow copy of the FTuple in O(1) time & space complexity."""
        return FTuple(*self)

    def map(self, f: Callable[[_T], _S]) -> FTuple[_S]:
        return FTuple(*map(f, self))

    def __add__(self, other: FTuple[_T]) -> FTuple[_T]:
        """Concatenate two FTuples."""
        return FTuple(*concat(iter(self), other))

    def __mul__(self, num: int) -> FTuple[_T]:
        """Return an `FTuple` which repeats another `FTuple` `num` times."""
        return FTuple(*self._tuple.__mul__(num if num > 0 else 0))

    def accummulate(self, f: Callable[[_T, _T], _T]) -> FTuple[_T]:
        """Accumulate partial fold results in an FTuple."""
        return FTuple(*accumulate(self, f))

    # def accummulate1(self, f: Callable[[_S, _T], _S], s: _S) -> FTuple[_S]:
    #     """Accumulate partial fold results in an FTuple with an initial value."""
    #     return FTuple(*accumulate(chain((s,), self), f))

    def flatMap(self, f: Callable[[_T], FTuple[_S]]) -> FTuple[_S]:
        """Monadically bind `f` to the data structure sequentially."""
        return FTuple(*concat(*map(lambda x: iter(x), map(f, self))))

    def mergeMap(self, f: Callable[[_T], FTuple[_S]]) -> FTuple[_S]:
        """Monadically bind `f` to the data structure, merge until one exhausted."""
        return FTuple(*merge(*map(lambda x: iter(x), map(f, self))))

    def exhaustMap(self, f: Callable[[_T], FTuple[_S]]) -> FTuple[_S]:
        """Monadically bind `f` to the data structure, merge until all are exhausted."""
        return FTuple(*exhaust(*map(lambda x: iter(x), map(f, self))))

Ancestors

  • typing.Generic

Methods

def accummulate(self, f: Callable[[_T, _T], _T]) ‑> FTuple[_T]

Accumulate partial fold results in an FTuple.

def copy(self) ‑> FTuple[_T]

Return shallow copy of the FTuple in O(1) time & space complexity.

def exhaustMap(self, f: Callable[[_T], FTuple[_S]]) ‑> FTuple[_S]

Monadically bind f to the data structure, merge until all are exhausted.

def flatMap(self, f: Callable[[_T], FTuple[_S]]) ‑> FTuple[_S]

Monadically bind f to the data structure sequentially.

def foldL(self, f: Callable[[_T, _T], _T]) ‑> Optional[_T]

Fold (reduce) left.

  • first argument of f is for the accumulated value
  • if FTuple is empty, return None
def foldL1(self, f: Callable[[_S, _T], _S], s: _S) ‑> _S

Fold left with an initial value.

  • first argument of f is for the accumulated value
  • if empty, return the initial value s
def foldR(self, f: Callable[[_T, _T], _T]) ‑> Optional[_T]

Fold (reduce) right.

  • second argument of f is for the accumulated value
  • if FTuple is empty, return None
def foldR1(self, f: Callable[[_T, _S], _S], s: _S) ‑> _S

Fold right with an initial value.

  • second argument of f is for the accumulated value
  • if empty, return the initial value s
def map(self, f: Callable[[_T], _S]) ‑> FTuple[_S]
def mergeMap(self, f: Callable[[_T], FTuple[_S]]) ‑> FTuple[_S]

Monadically bind f to the data structure, merge until one exhausted.