pythonic-fp.containers 3.0.0__py3-none-any.whl → 4.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pythonic_fp/containers/__init__.py +10 -14
- pythonic_fp/containers/functional_tuple.py +52 -51
- pythonic_fp/containers/functional_tuple.pyi +22 -0
- pythonic_fp/containers/immutable_list.py +51 -55
- pythonic_fp/containers/immutable_list.pyi +28 -0
- pythonic_fp_containers-4.0.0.dist-info/METADATA +62 -0
- pythonic_fp_containers-4.0.0.dist-info/RECORD +11 -0
- pythonic_fp/containers/queues/__init__.py +0 -31
- pythonic_fp/containers/queues/de.py +0 -180
- pythonic_fp/containers/queues/fifo.py +0 -145
- pythonic_fp/containers/queues/lifo.py +0 -136
- pythonic_fp_containers-3.0.0.dist-info/METADATA +0 -63
- pythonic_fp_containers-3.0.0.dist-info/RECORD +0 -13
- /pythonic_fp/containers/{queues/py.typed → __init__.pyi} +0 -0
- {pythonic_fp_containers-3.0.0.dist-info → pythonic_fp_containers-4.0.0.dist-info}/WHEEL +0 -0
- {pythonic_fp_containers-3.0.0.dist-info → pythonic_fp_containers-4.0.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -12,21 +12,17 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
| Description | Class | Package/Module |
|
|
19
|
-
+==================+========+====================================================+
|
|
20
|
-
| Functional tuple | FTuple | module ``pythonic_fp.containers.functional_tuple`` |
|
|
21
|
-
+------------------+--------+----------------------------------------------------+
|
|
22
|
-
| Immutable list | IList | module ``pythonic_fp.containers.immutable_list`` |
|
|
23
|
-
+------------------+--------+----------------------------------------------------+
|
|
24
|
-
| Queues | | package ``pythonic_fp.containers.queues`` |
|
|
25
|
-
+------------------+--------+----------------------------------------------------+
|
|
26
|
-
|
|
27
|
-
.. note::
|
|
15
|
+
"""
|
|
16
|
+
Container with FP interfaces
|
|
17
|
+
============================
|
|
28
18
|
|
|
29
|
-
|
|
19
|
+
+---------------------------------------------+-----------+------------------+
|
|
20
|
+
| module | class | description |
|
|
21
|
+
+=============================================+===========+==================+
|
|
22
|
+
| ``pythonic_fp.containers.functional_tuple`` | FTuple | Functional Tuple |
|
|
23
|
+
+---------------------------------------------+-----------+------------------+
|
|
24
|
+
| ``pythonic_fp.containers.immutable_list`` | IList | Immutable List |
|
|
25
|
+
+---------------------------------------------+-----------+------------------+
|
|
30
26
|
|
|
31
27
|
"""
|
|
32
28
|
|
|
@@ -14,22 +14,19 @@
|
|
|
14
14
|
|
|
15
15
|
"""Pythonic FP - Functional Tuple"""
|
|
16
16
|
|
|
17
|
-
from __future__ import annotations
|
|
18
|
-
|
|
19
17
|
from collections.abc import Callable, Iterator
|
|
20
|
-
from typing import cast, Never, overload,
|
|
18
|
+
from typing import cast, Never, overload, SupportsIndex
|
|
21
19
|
from pythonic_fp.iterables.folding import accumulate
|
|
22
20
|
from pythonic_fp.iterables.merging import blend, concat, MergeEnum
|
|
23
21
|
|
|
24
22
|
__all__ = ['FTuple']
|
|
25
23
|
|
|
26
|
-
D = TypeVar('D', covariant=True)
|
|
27
24
|
|
|
28
25
|
class FTuple[D](tuple[D, ...]):
|
|
29
|
-
"""Functional Tuple suitable for inheritance
|
|
26
|
+
"""Functional Tuple suitable for inheritance.
|
|
30
27
|
|
|
31
|
-
-
|
|
32
|
-
-
|
|
28
|
+
- supports both indexing and slicing
|
|
29
|
+
- int multiplication and FTuple addition supported
|
|
33
30
|
|
|
34
31
|
- addition concatenates results, resulting in a Union type
|
|
35
32
|
- both left and right int multiplication supported
|
|
@@ -37,9 +34,9 @@ class FTuple[D](tuple[D, ...]):
|
|
|
37
34
|
- supports being further inherited from
|
|
38
35
|
- unslotted
|
|
39
36
|
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
Since these tuples are homogeneous, their covariance may be quirky.
|
|
42
38
|
"""
|
|
39
|
+
|
|
43
40
|
def __reversed__(self) -> Iterator[D]:
|
|
44
41
|
for ii in range(len(self) - 1, -1, -1):
|
|
45
42
|
yield (self[ii])
|
|
@@ -59,17 +56,14 @@ class FTuple[D](tuple[D, ...]):
|
|
|
59
56
|
return False
|
|
60
57
|
return True
|
|
61
58
|
|
|
62
|
-
@overload # type:ignore
|
|
63
|
-
def __getitem__(self, x: int) -> D: ...
|
|
64
59
|
@overload
|
|
65
|
-
def __getitem__(self,
|
|
66
|
-
|
|
67
|
-
def __getitem__(self, idx:
|
|
68
|
-
def newtup(tup: tuple[D, ...]) -> tuple[D, ...]:
|
|
69
|
-
return tup[0:-1] + tup[-1:]
|
|
60
|
+
def __getitem__(self, idx: SupportsIndex) -> D: ...
|
|
61
|
+
@overload
|
|
62
|
+
def __getitem__(self, idx: slice) -> tuple[D, ...]: ...
|
|
70
63
|
|
|
64
|
+
def __getitem__(self, idx: SupportsIndex | slice) -> tuple[D, ...] | D:
|
|
71
65
|
if isinstance(idx, slice):
|
|
72
|
-
return self.__class__(
|
|
66
|
+
return self.__class__(super().__getitem__(idx))
|
|
73
67
|
else:
|
|
74
68
|
return super().__getitem__(idx)
|
|
75
69
|
|
|
@@ -79,14 +73,17 @@ class FTuple[D](tuple[D, ...]):
|
|
|
79
73
|
/,
|
|
80
74
|
start: L | None = None,
|
|
81
75
|
default: L | None = None,
|
|
82
|
-
) -> L
|
|
83
|
-
"""Fold Left
|
|
76
|
+
) -> L:
|
|
77
|
+
"""Fold Left with optional starting and default values.
|
|
84
78
|
|
|
85
79
|
- fold left with an optional starting value
|
|
86
|
-
- first argument of function f is for the accumulated value
|
|
87
|
-
|
|
88
|
-
:raises ValueError: when FTuple empty and a start value not given
|
|
80
|
+
- first argument of function ``f`` is for the accumulated value
|
|
89
81
|
|
|
82
|
+
:param f: The folding function, first argument is for the accumulated value.
|
|
83
|
+
:param start: An optional starting value.
|
|
84
|
+
:param default: An optional default value if fold does not exist.
|
|
85
|
+
:raises ValueError: When ``FTuple`` empty and a start value not given.
|
|
86
|
+
:returns: The folded value if it exists, otherwise the default value.
|
|
90
87
|
"""
|
|
91
88
|
it_self = iter(self)
|
|
92
89
|
if start is not None:
|
|
@@ -108,14 +105,14 @@ class FTuple[D](tuple[D, ...]):
|
|
|
108
105
|
/,
|
|
109
106
|
start: R | None = None,
|
|
110
107
|
default: R | None = None,
|
|
111
|
-
) -> R
|
|
112
|
-
"""Fold Right
|
|
113
|
-
|
|
114
|
-
- fold right with an optional starting value
|
|
115
|
-
- second argument of function f is for the accumulated value
|
|
108
|
+
) -> R:
|
|
109
|
+
"""Fold Right with optional starting and default values.
|
|
116
110
|
|
|
111
|
+
:param f: The folding function, second argument is for the accumulated value.
|
|
112
|
+
:param start: An optional starting value.
|
|
113
|
+
:param default: An optional default value if fold does not exist.
|
|
117
114
|
:raises ValueError: when FTuple empty and a start value not given
|
|
118
|
-
|
|
115
|
+
:returns: The folded value if it exists, otherwise the default value.
|
|
119
116
|
"""
|
|
120
117
|
it_self = reversed(self)
|
|
121
118
|
if start is not None:
|
|
@@ -131,54 +128,58 @@ class FTuple[D](tuple[D, ...]):
|
|
|
131
128
|
acc = f(v, acc)
|
|
132
129
|
return acc
|
|
133
130
|
|
|
134
|
-
def copy(self) -> FTuple[D]:
|
|
135
|
-
"""Return a shallow copy of ``FTuple`` in O(1) time & space complexity.
|
|
131
|
+
def copy(self) -> 'FTuple[D]':
|
|
132
|
+
"""Return a shallow copy of ``FTuple`` in O(1) time & space complexity.
|
|
133
|
+
|
|
134
|
+
:returns: New ``FTuple``.
|
|
135
|
+
"""
|
|
136
136
|
return self.__class__(self)
|
|
137
137
|
|
|
138
138
|
def __add__(self, other: object, /) -> tuple[D, ...]:
|
|
139
|
-
if not isinstance(other,
|
|
139
|
+
if not isinstance(other, tuple):
|
|
140
140
|
msg = 'FTuple being added to something not an FTuple'
|
|
141
141
|
raise ValueError(msg)
|
|
142
142
|
return self.__class__(concat(self, other))
|
|
143
143
|
|
|
144
|
-
def __mul__(self, num:
|
|
144
|
+
def __mul__(self, num: SupportsIndex) -> tuple[D, ...]:
|
|
145
145
|
return self.__class__(super().__mul__(num))
|
|
146
146
|
|
|
147
|
-
def __rmul__(self, num:
|
|
147
|
+
def __rmul__(self, num: SupportsIndex) -> tuple[D, ...]:
|
|
148
148
|
return self.__class__(super().__rmul__(num))
|
|
149
149
|
|
|
150
150
|
def accummulate[L](
|
|
151
151
|
self, f: Callable[[L, D], L], s: L | None = None, /
|
|
152
|
-
) -> FTuple[L]:
|
|
153
|
-
"""Accumulate partial folds
|
|
152
|
+
) -> 'FTuple[L]':
|
|
153
|
+
"""Accumulate partial folds.
|
|
154
154
|
|
|
155
|
-
Accumulate partial fold
|
|
156
|
-
|
|
155
|
+
Accumulate partial fold with an optional starting value,
|
|
156
|
+
results in an ``FTuple``.
|
|
157
157
|
|
|
158
|
+
:param f: Folding function used to produce partial folds.
|
|
159
|
+
:param s: Optional starting value.
|
|
160
|
+
:returns: New ``FTuple`` of the partial folds.
|
|
158
161
|
"""
|
|
159
162
|
if s is None:
|
|
160
163
|
return FTuple(accumulate(self, f))
|
|
161
164
|
return FTuple(accumulate(self, f, s))
|
|
162
165
|
|
|
163
|
-
def map[U](self, f: Callable[[D], U], /) -> FTuple[U]:
|
|
166
|
+
def map[U](self, f: Callable[[D], U], /) -> 'FTuple[U]':
|
|
164
167
|
return FTuple(map(f, self))
|
|
165
168
|
|
|
166
169
|
def bind[U](
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
170
|
+
self,
|
|
171
|
+
f: 'Callable[[D], FTuple[U]]',
|
|
172
|
+
merge_type: MergeEnum = MergeEnum.Concat,
|
|
173
|
+
yield_partials: bool = False,
|
|
174
|
+
) -> 'FTuple[U] | Never':
|
|
171
175
|
"""Bind function ``f`` to the ``FTuple``.
|
|
172
176
|
|
|
173
|
-
:param
|
|
174
|
-
:
|
|
175
|
-
:
|
|
176
|
-
|
|
177
|
+
:param f: Function ``D -> FTuple[U]``
|
|
178
|
+
:param merge_type: ``MergeEnum`` to determine how to merge the result.
|
|
179
|
+
:param yield_partials: Yield unmatched values if ``MergeEnum`` given as merge type.
|
|
180
|
+
:return: Resulting ``FTuple``.
|
|
181
|
+
:raises ValueError: If given an unknown merge enumeration.
|
|
177
182
|
"""
|
|
178
183
|
return FTuple(
|
|
179
|
-
blend(
|
|
180
|
-
*map(f, self),
|
|
181
|
-
merge_enum=merge_enum,
|
|
182
|
-
yield_partials=yield_partials
|
|
183
|
-
)
|
|
184
|
+
blend(*map(f, self), merge_enum=merge_type, yield_partials=yield_partials)
|
|
184
185
|
)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from collections.abc import Callable, Iterator
|
|
2
|
+
from pythonic_fp.iterables.merging import MergeEnum
|
|
3
|
+
from typing import Never, SupportsIndex, overload
|
|
4
|
+
|
|
5
|
+
__all__ = ['FTuple']
|
|
6
|
+
|
|
7
|
+
class FTuple[D](tuple[D, ...]):
|
|
8
|
+
def __reversed__(self) -> Iterator[D]: ...
|
|
9
|
+
def __eq__(self, other: object) -> bool: ...
|
|
10
|
+
@overload
|
|
11
|
+
def __getitem__(self, idx: SupportsIndex) -> D: ...
|
|
12
|
+
@overload
|
|
13
|
+
def __getitem__(self, idx: slice) -> tuple[D, ...]: ...
|
|
14
|
+
def foldl[L](self, f: Callable[[L, D], L], /, start: L | None = None, default: L | None = None) -> L: ...
|
|
15
|
+
def foldr[R](self, f: Callable[[D, R], R], /, start: R | None = None, default: R | None = None) -> R: ...
|
|
16
|
+
def copy(self) -> FTuple[D]: ...
|
|
17
|
+
def __add__(self, other: object) -> tuple[D, ...]: ...
|
|
18
|
+
def __mul__(self, num: SupportsIndex) -> tuple[D, ...]: ...
|
|
19
|
+
def __rmul__(self, num: SupportsIndex) -> tuple[D, ...]: ...
|
|
20
|
+
def accummulate[L](self, f: Callable[[L, D], L], s: L | None = None, /) -> FTuple[L]: ...
|
|
21
|
+
def map[U](self, f: Callable[[D], U], /) -> FTuple[U]: ...
|
|
22
|
+
def bind[U](self, f: Callable[[D], FTuple[U]], merge_type: MergeEnum = ..., yield_partials: bool = False) -> FTuple[U] | Never: ...
|
|
@@ -12,47 +12,36 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
"""Pythonic FP - Immutable guaranteed hashable lists
|
|
16
|
-
|
|
17
|
-
- hashable if elements are hashable
|
|
18
|
-
- declared covariant in its generic datatype
|
|
19
|
-
- hashability should be enforced by LSP tooling
|
|
20
|
-
- hashability will be enforced at runtime
|
|
21
|
-
- IList addition supported via concatenation
|
|
22
|
-
- IList integer multiplication supported
|
|
23
|
-
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
from __future__ import annotations
|
|
15
|
+
"""Pythonic FP - Immutable guaranteed hashable lists."""
|
|
27
16
|
|
|
28
17
|
from collections.abc import Callable, Iterable, Iterator, Hashable
|
|
29
|
-
from typing import cast, Never, overload
|
|
18
|
+
from typing import cast, Never, overload
|
|
30
19
|
from pythonic_fp.iterables.folding import accumulate
|
|
31
20
|
from pythonic_fp.iterables.merging import blend, concat, MergeEnum
|
|
32
21
|
|
|
33
22
|
__all__ = ['IList']
|
|
34
23
|
|
|
35
|
-
D = TypeVar('D', covariant=True)
|
|
36
|
-
T = TypeVar('T')
|
|
37
24
|
|
|
38
25
|
class IList[D](Hashable):
|
|
39
26
|
"""Immutable List like data structure.
|
|
40
27
|
|
|
28
|
+
- hashability should be enforced by LSP tooling
|
|
29
|
+
- hashability will be enforced at runtime
|
|
41
30
|
- its method type parameters are also covariant
|
|
42
|
-
- hashability will be enforced by LSP tooling
|
|
43
31
|
- supports both indexing and slicing
|
|
44
|
-
- addition
|
|
45
|
-
|
|
32
|
+
- addition and left & right ``int`` multiplication supported
|
|
33
|
+
|
|
34
|
+
- addition is concatenation resulting in a union type
|
|
46
35
|
|
|
47
36
|
"""
|
|
37
|
+
|
|
48
38
|
__slots__ = ('_ds', '_len', '_hash')
|
|
49
39
|
__match_args__ = ('_ds', '_len')
|
|
50
40
|
|
|
51
|
-
L = TypeVar('L')
|
|
52
|
-
R = TypeVar('R')
|
|
53
|
-
U = TypeVar('U')
|
|
54
|
-
|
|
55
41
|
def __init__(self, *dss: Iterable[D]) -> None:
|
|
42
|
+
"""
|
|
43
|
+
:param dss: 0 or 1 iterables
|
|
44
|
+
"""
|
|
56
45
|
if (size := len(dss)) > 1:
|
|
57
46
|
msg = f'IList expects at most 1 iterable argument, got {size}'
|
|
58
47
|
raise ValueError(msg)
|
|
@@ -98,9 +87,9 @@ class IList[D](Hashable):
|
|
|
98
87
|
@overload
|
|
99
88
|
def __getitem__(self, idx: int, /) -> D: ...
|
|
100
89
|
@overload
|
|
101
|
-
def __getitem__(self, idx: slice, /) -> IList[D]: ...
|
|
90
|
+
def __getitem__(self, idx: slice, /) -> 'IList[D]': ...
|
|
102
91
|
|
|
103
|
-
def __getitem__(self, idx: slice | int, /) -> IList[D] | D:
|
|
92
|
+
def __getitem__(self, idx: slice | int, /) -> 'IList[D] | D':
|
|
104
93
|
if isinstance(idx, slice):
|
|
105
94
|
return IList(self._ds[idx])
|
|
106
95
|
return self._ds[idx]
|
|
@@ -112,13 +101,13 @@ class IList[D](Hashable):
|
|
|
112
101
|
start: L | None = None,
|
|
113
102
|
default: L | None = None,
|
|
114
103
|
) -> L | None:
|
|
115
|
-
"""Fold Left
|
|
116
|
-
|
|
117
|
-
- fold left with an optional starting value
|
|
118
|
-
- first argument of function ``f`` is for the accumulated value
|
|
119
|
-
|
|
120
|
-
"raises ValueError: when empty and a start value not given
|
|
104
|
+
"""Fold Left.
|
|
121
105
|
|
|
106
|
+
:param f: Folding function, first argument is for the accumulated value.
|
|
107
|
+
:param start: Optional starting value.
|
|
108
|
+
:param default: Optional default value if fold does not exist.
|
|
109
|
+
:returns: Folded value.
|
|
110
|
+
:raises ValueError: When empty and a start value not given.
|
|
122
111
|
"""
|
|
123
112
|
it = iter(self._ds)
|
|
124
113
|
if start is not None:
|
|
@@ -143,13 +132,13 @@ class IList[D](Hashable):
|
|
|
143
132
|
start: R | None = None,
|
|
144
133
|
default: R | None = None,
|
|
145
134
|
) -> R | None:
|
|
146
|
-
"""Fold Right
|
|
147
|
-
|
|
148
|
-
- fold right with an optional starting value
|
|
149
|
-
- second argument of function ``f`` is for the accumulated value
|
|
150
|
-
|
|
151
|
-
"raises ValueError: when empty and a start value not given
|
|
135
|
+
"""Fold Right.
|
|
152
136
|
|
|
137
|
+
:param f: Folding function, second argument is for the accumulated value.
|
|
138
|
+
:param start: Optional starting value.
|
|
139
|
+
:param default: Optional default value if fold does not exist.
|
|
140
|
+
:returns: Folded value.
|
|
141
|
+
:raises ValueError: When empty and a start value not given.
|
|
153
142
|
"""
|
|
154
143
|
it = reversed(self._ds)
|
|
155
144
|
if start is not None:
|
|
@@ -167,52 +156,59 @@ class IList[D](Hashable):
|
|
|
167
156
|
acc = f(v, acc)
|
|
168
157
|
return acc
|
|
169
158
|
|
|
170
|
-
def __add__(self, other: IList[D], /) -> IList[D]:
|
|
159
|
+
def __add__(self, other: 'IList[D]', /) -> 'IList[D]':
|
|
171
160
|
if not isinstance(other, IList):
|
|
172
|
-
msg = 'IList being added to something not
|
|
161
|
+
msg = 'IList being added to something not an IList'
|
|
173
162
|
raise ValueError(msg)
|
|
174
163
|
|
|
175
164
|
return IList(concat(self, other))
|
|
176
165
|
|
|
177
|
-
def __mul__(self, num: int, /) -> IList[D]:
|
|
166
|
+
def __mul__(self, num: int, /) -> 'IList[D]':
|
|
178
167
|
return IList(self._ds.__mul__(num if num > 0 else 0))
|
|
179
168
|
|
|
180
|
-
def __rmul__(self, num: int, /) -> IList[D]:
|
|
169
|
+
def __rmul__(self, num: int, /) -> 'IList[D]':
|
|
181
170
|
return IList(self._ds.__mul__(num if num > 0 else 0))
|
|
182
171
|
|
|
183
172
|
def accummulate[L](
|
|
184
173
|
self, f: Callable[[L, D], L], s: L | None = None, /
|
|
185
|
-
) -> IList[L]:
|
|
186
|
-
"""Accumulate partial folds
|
|
174
|
+
) -> 'IList[L]':
|
|
175
|
+
"""Accumulate partial folds.
|
|
187
176
|
|
|
188
|
-
Accumulate partial fold
|
|
189
|
-
|
|
177
|
+
Accumulate partial fold with an optional starting value,
|
|
178
|
+
results in an ``IList``.
|
|
190
179
|
|
|
180
|
+
:param f: Folding function used to produce partial folds.
|
|
181
|
+
:param s: Optional starting value.
|
|
182
|
+
:returns: New ``FTuple`` of the partial folds.
|
|
191
183
|
"""
|
|
192
184
|
if s is None:
|
|
193
185
|
return IList(accumulate(self, f))
|
|
194
186
|
return IList(accumulate(self, f, s))
|
|
195
187
|
|
|
196
|
-
def map[U](self, f: Callable[[D], U], /) -> IList[U]:
|
|
188
|
+
def map[U](self, f: Callable[[D], U], /) -> 'IList[U]':
|
|
197
189
|
return IList(map(f, self))
|
|
198
190
|
|
|
199
191
|
def bind[U](
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
192
|
+
self,
|
|
193
|
+
f: 'Callable[[D], IList[U]]',
|
|
194
|
+
merge_enum: MergeEnum = MergeEnum.Concat,
|
|
195
|
+
yield_partials: bool = False,
|
|
196
|
+
) -> 'IList[U] | Never':
|
|
205
197
|
"""Bind function `f` to the `IList`.
|
|
206
198
|
|
|
207
199
|
:param ds: values to instantiate IList
|
|
208
200
|
:return: resulting IList
|
|
209
201
|
:raises ValueError: if given unknown merge_type
|
|
210
202
|
|
|
203
|
+
"""
|
|
204
|
+
"""Bind function ``f`` to the ``IList``.
|
|
205
|
+
|
|
206
|
+
:param f: Function ``D -> IList[U]``
|
|
207
|
+
:param merge_type: ``MergeEnum`` to determine how to merge the result.
|
|
208
|
+
:param yield_partials: Yield unmatched values if ``MergeEnum`` given as merge type.
|
|
209
|
+
:return: Resulting ``IList``.
|
|
210
|
+
:raises ValueError: If given an unknown merge enumeration.
|
|
211
211
|
"""
|
|
212
212
|
return IList(
|
|
213
|
-
blend(
|
|
214
|
-
*map(f, self),
|
|
215
|
-
merge_enum=merge_enum,
|
|
216
|
-
yield_partials=yield_partials
|
|
217
|
-
)
|
|
213
|
+
blend(*map(f, self), merge_enum=merge_enum, yield_partials=yield_partials)
|
|
218
214
|
)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from _typeshed import Incomplete
|
|
2
|
+
from collections.abc import Callable, Hashable, Iterable, Iterator
|
|
3
|
+
from pythonic_fp.iterables.merging import MergeEnum
|
|
4
|
+
from typing import Never, overload
|
|
5
|
+
|
|
6
|
+
__all__ = ['IList']
|
|
7
|
+
|
|
8
|
+
class IList[D](Hashable):
|
|
9
|
+
__match_args__: Incomplete
|
|
10
|
+
def __init__(self, *dss: Iterable[D]) -> None: ...
|
|
11
|
+
def __hash__(self) -> int: ...
|
|
12
|
+
def __iter__(self) -> Iterator[D]: ...
|
|
13
|
+
def __reversed__(self) -> Iterator[D]: ...
|
|
14
|
+
def __bool__(self) -> bool: ...
|
|
15
|
+
def __len__(self) -> int: ...
|
|
16
|
+
def __eq__(self, other: object) -> bool: ...
|
|
17
|
+
@overload
|
|
18
|
+
def __getitem__(self, idx: int) -> D: ...
|
|
19
|
+
@overload
|
|
20
|
+
def __getitem__(self, idx: slice) -> IList[D]: ...
|
|
21
|
+
def foldl[L](self, f: Callable[[L, D], L], /, start: L | None = None, default: L | None = None) -> L | None: ...
|
|
22
|
+
def foldr[R](self, f: Callable[[D, R], R], /, start: R | None = None, default: R | None = None) -> R | None: ...
|
|
23
|
+
def __add__(self, other: IList[D]) -> IList[D]: ...
|
|
24
|
+
def __mul__(self, num: int) -> IList[D]: ...
|
|
25
|
+
def __rmul__(self, num: int) -> IList[D]: ...
|
|
26
|
+
def accummulate[L](self, f: Callable[[L, D], L], s: L | None = None, /) -> IList[L]: ...
|
|
27
|
+
def map[U](self, f: Callable[[D], U], /) -> IList[U]: ...
|
|
28
|
+
def bind[U](self, f: Callable[[D], IList[U]], merge_enum: MergeEnum = ..., yield_partials: bool = False) -> IList[U] | Never: ...
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pythonic-fp-containers
|
|
3
|
+
Version: 4.0.0
|
|
4
|
+
Summary: Container with FP interfaces
|
|
5
|
+
Keywords: functional_containers,functional_tuple,immutable_list
|
|
6
|
+
Author-email: "Geoffrey R. Scheller" <geoffrey@scheller.com>
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Description-Content-Type: text/x-rst
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Framework :: Pytest
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Typing :: Typed
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: pythonic-fp-circulararray>=6.0.0
|
|
18
|
+
Requires-Dist: pythonic-fp-fptools>=5.1.2
|
|
19
|
+
Requires-Dist: pythonic-fp-iterables>=5.1.2
|
|
20
|
+
Requires-Dist: pytest>=8.4.1 ; extra == "test"
|
|
21
|
+
Project-URL: Changelog, https://github.com/grscheller/pythonic-fp-containers/blob/main/CHANGELOG.rst
|
|
22
|
+
Project-URL: Documentation, https://grscheller.github.io/pythonic-fp/containers/development/build/html/
|
|
23
|
+
Project-URL: Homepage, https://grscheller.github.io/pythonic-fp/homepage/build/html/
|
|
24
|
+
Project-URL: Source, https://github.com/grscheller/pythonic-fp-containers
|
|
25
|
+
Provides-Extra: test
|
|
26
|
+
|
|
27
|
+
Pythonic FP - Containers
|
|
28
|
+
========================
|
|
29
|
+
|
|
30
|
+
PyPI project
|
|
31
|
+
`pythonic-fp.containers
|
|
32
|
+
<https://pypi.org/project/pythonic-fp.containers>`_.
|
|
33
|
+
|
|
34
|
+
Python package of container like data structures with functional
|
|
35
|
+
programming inspired tweaks.
|
|
36
|
+
|
|
37
|
+
+---------------------------------------------+-----------+------------------+
|
|
38
|
+
| module | class | name |
|
|
39
|
+
+=============================================+===========+==================+
|
|
40
|
+
| ``pythonic_fp.containers.functional_tuple`` | FTuple | Functional Tuple |
|
|
41
|
+
+---------------------------------------------+-----------+------------------+
|
|
42
|
+
| ``pythonic_fp.containers.immutable_list`` | IList | Immutable List |
|
|
43
|
+
+---------------------------------------------+-----------+------------------+
|
|
44
|
+
|
|
45
|
+
Part of the
|
|
46
|
+
`pythonic-fp
|
|
47
|
+
<https://grscheller.github.io/pythonic-fp/homepage/build/html/index.html>`_
|
|
48
|
+
PyPI projects.
|
|
49
|
+
|
|
50
|
+
Documentation
|
|
51
|
+
-------------
|
|
52
|
+
|
|
53
|
+
Documentation for this project is hosted on
|
|
54
|
+
`GitHub Pages
|
|
55
|
+
<https://grscheller.github.io/pythonic-fp/containers/development/build/html>`_.
|
|
56
|
+
|
|
57
|
+
Copyright and License
|
|
58
|
+
---------------------
|
|
59
|
+
|
|
60
|
+
Copyright (c) 2023-2025 Geoffrey R. Scheller. Licensed under the Apache
|
|
61
|
+
License, Version 2.0. See the LICENSE file for details.
|
|
62
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
pythonic_fp/containers/__init__.py,sha256=48lsc3kU2Yf5IKjeExlOtO2ISwZv_1SWyL-e9OtP3VI,1346
|
|
2
|
+
pythonic_fp/containers/__init__.pyi,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
pythonic_fp/containers/functional_tuple.py,sha256=aSFAnuAinT6XgLsRwh2JX1J6a6fFOJYS5N3TAUH5u0s,6518
|
|
4
|
+
pythonic_fp/containers/functional_tuple.pyi,sha256=g8IvlIhPxecEsetEexZ7WmN9Ifdh-vAeK0z9SwLiCGE,1182
|
|
5
|
+
pythonic_fp/containers/immutable_list.py,sha256=SoPYqIRgXbhfVC5dP9x1xYOIZqUrcIPH317A5bIPPos,7065
|
|
6
|
+
pythonic_fp/containers/immutable_list.pyi,sha256=HRm0Updbx9UJpl5RI0prOwimSHDUJtM-3t2hEmYz6kw,1372
|
|
7
|
+
pythonic_fp/containers/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
pythonic_fp_containers-4.0.0.dist-info/licenses/LICENSE,sha256=IJMkVWQUVy-SonK6SMaMotDQ6Zb-lVi2bUTiGp1GUO8,10774
|
|
9
|
+
pythonic_fp_containers-4.0.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
10
|
+
pythonic_fp_containers-4.0.0.dist-info/METADATA,sha256=sTGVrVvUqjf7KyG-FzkvpdcMrzuaWLomCDKoRjwbPDE,2438
|
|
11
|
+
pythonic_fp_containers-4.0.0.dist-info/RECORD,,
|
|
@@ -1,31 +0,0 @@
|
|
|
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
|
-
"""Three types of queues.
|
|
16
|
-
|
|
17
|
-
+------------------------------------+-----------+--------------------------+
|
|
18
|
-
| module | Class | description |
|
|
19
|
-
+====================================+===========+==========================+
|
|
20
|
-
| pythonic_fp.containers.queues.fifo | FIFOQueue | First-In-First-Out queue |
|
|
21
|
-
+------------------------------------+-----------+--------------------------+
|
|
22
|
-
| pythonic_fp.containers.queues.lifo | LIFOQueue | Last-In-First-Out queue |
|
|
23
|
-
+------------------------------------+-----------+--------------------------+
|
|
24
|
-
| pythonic_fp.containers.queues.de | DEQueue | Double-Ended queue |
|
|
25
|
-
+------------------------------------+-----------+--------------------------+
|
|
26
|
-
|
|
27
|
-
"""
|
|
28
|
-
|
|
29
|
-
__author__ = 'Geoffrey R. Scheller'
|
|
30
|
-
__copyright__ = 'Copyright (c) 2023-2025 Geoffrey R. Scheller'
|
|
31
|
-
__license__ = 'Apache License 2.0'
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
# Copyright 2023-2025 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
|
-
"""Stateful Double-Ended (DE) Queue data structure.
|
|
16
|
-
|
|
17
|
-
- O(1) pops each end
|
|
18
|
-
- O(1) amortized pushes each end
|
|
19
|
-
- O(1) length determination
|
|
20
|
-
- will automatically increase storage capacity when needed
|
|
21
|
-
- in a Boolean context, true if not empty, false if empty
|
|
22
|
-
- neither indexable nor sliceable by design
|
|
23
|
-
|
|
24
|
-
"""
|
|
25
|
-
from __future__ import annotations
|
|
26
|
-
|
|
27
|
-
from collections.abc import Callable, Iterable, Iterator
|
|
28
|
-
from typing import TypeVar
|
|
29
|
-
from pythonic_fp.circulararray.auto import CA
|
|
30
|
-
from pythonic_fp.fptools.maybe import MayBe
|
|
31
|
-
|
|
32
|
-
__all__ = ['DEQueue', 'de_queue']
|
|
33
|
-
|
|
34
|
-
D = TypeVar('D')
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
class DEQueue[D]:
|
|
38
|
-
|
|
39
|
-
__slots__ = ('_ca',)
|
|
40
|
-
|
|
41
|
-
def __init__(self, *dss: Iterable[D]) -> None:
|
|
42
|
-
"""Initial data in FIFO order, newest to oldest, as if
|
|
43
|
-
pushed on from the right side.
|
|
44
|
-
|
|
45
|
-
:param dss: takes up to one iterable
|
|
46
|
-
:raises ValueError: if more than 1 iterable is given
|
|
47
|
-
|
|
48
|
-
"""
|
|
49
|
-
if (size := len(dss)) > 1:
|
|
50
|
-
msg = f'DEQueue expects at most 1 argument, got {size}'
|
|
51
|
-
raise ValueError(msg)
|
|
52
|
-
self._ca = CA(dss[0]) if size == 1 else CA()
|
|
53
|
-
|
|
54
|
-
def __bool__(self) -> bool:
|
|
55
|
-
return len(self._ca) > 0
|
|
56
|
-
|
|
57
|
-
def __len__(self) -> int:
|
|
58
|
-
return len(self._ca)
|
|
59
|
-
|
|
60
|
-
def __eq__(self, other: object) -> bool:
|
|
61
|
-
if not isinstance(other, DEQueue):
|
|
62
|
-
return False
|
|
63
|
-
return self._ca == other._ca
|
|
64
|
-
|
|
65
|
-
def __iter__(self) -> Iterator[D]:
|
|
66
|
-
return iter(list(self._ca))
|
|
67
|
-
|
|
68
|
-
def __reversed__(self) -> Iterator[D]:
|
|
69
|
-
return reversed(list(self._ca))
|
|
70
|
-
|
|
71
|
-
def __repr__(self) -> str:
|
|
72
|
-
if len(self) == 0:
|
|
73
|
-
return 'DEQueue()'
|
|
74
|
-
return 'DEQueue(' + ', '.join(map(repr, self._ca)) + ')'
|
|
75
|
-
|
|
76
|
-
def __str__(self) -> str:
|
|
77
|
-
return '>< ' + ' | '.join(map(str, self)) + ' ><'
|
|
78
|
-
|
|
79
|
-
def copy(self) -> DEQueue[D]:
|
|
80
|
-
"""Shallow copy.
|
|
81
|
-
|
|
82
|
-
:return: shallow copy of the DEQueue
|
|
83
|
-
|
|
84
|
-
"""
|
|
85
|
-
return DEQueue(self._ca)
|
|
86
|
-
|
|
87
|
-
def pushl(self, *ds: D) -> None:
|
|
88
|
-
"""Push data onto left side of DEQueue.
|
|
89
|
-
|
|
90
|
-
:param ds: items to be pushed onto DEQueue from the left
|
|
91
|
-
|
|
92
|
-
"""
|
|
93
|
-
self._ca.pushl(*ds)
|
|
94
|
-
|
|
95
|
-
def pushr(self, *ds: D) -> None:
|
|
96
|
-
"""Push data onto right side of DEQueue.
|
|
97
|
-
|
|
98
|
-
:param ds: items to be pushed onto DEQueue from the right
|
|
99
|
-
|
|
100
|
-
"""
|
|
101
|
-
self._ca.pushr(*ds)
|
|
102
|
-
|
|
103
|
-
def popl(self) -> MayBe[D]:
|
|
104
|
-
"""Pop next data item from left side DEQueue, if it exists.
|
|
105
|
-
|
|
106
|
-
:return: MayBe of popped item if queue was not empty, empty MayBe otherwise
|
|
107
|
-
|
|
108
|
-
"""
|
|
109
|
-
if self._ca:
|
|
110
|
-
return MayBe(self._ca.popl())
|
|
111
|
-
return MayBe()
|
|
112
|
-
|
|
113
|
-
def popr(self) -> MayBe[D]:
|
|
114
|
-
"""Pop next item off right side DEQueue.
|
|
115
|
-
|
|
116
|
-
:return: MayBe of popped item if queue was not empty, empty MayBe otherwise
|
|
117
|
-
|
|
118
|
-
"""
|
|
119
|
-
if self._ca:
|
|
120
|
-
return MayBe(self._ca.popr())
|
|
121
|
-
return MayBe()
|
|
122
|
-
|
|
123
|
-
def peakl(self) -> MayBe[D]:
|
|
124
|
-
"""Peak at data on left side of DEQueue.
|
|
125
|
-
|
|
126
|
-
:return: MayBe of leftmost data if queue not empty, empty MayBe otherwise
|
|
127
|
-
|
|
128
|
-
"""
|
|
129
|
-
if self._ca:
|
|
130
|
-
return MayBe(self._ca[0])
|
|
131
|
-
return MayBe()
|
|
132
|
-
|
|
133
|
-
def peakr(self) -> MayBe[D]:
|
|
134
|
-
"""Peak at right side of DEQueue. Does not consume item.
|
|
135
|
-
|
|
136
|
-
:return: MayBe of rightmost data if queue not empty, empty MayBe otherwise
|
|
137
|
-
|
|
138
|
-
"""
|
|
139
|
-
if self._ca:
|
|
140
|
-
return MayBe(self._ca[-1])
|
|
141
|
-
return MayBe()
|
|
142
|
-
|
|
143
|
-
def foldl[L](self, f: Callable[[L, D], L], start: L | None = None) -> MayBe[L]:
|
|
144
|
-
"""Reduces DEQueue left to right.
|
|
145
|
-
|
|
146
|
-
:param f: reducing function, first argument is for accumulator
|
|
147
|
-
:param start: optional starting value
|
|
148
|
-
:return: MayBe of reduced value with f, empty MayBe if queue empty and no starting value given
|
|
149
|
-
|
|
150
|
-
"""
|
|
151
|
-
if start is None:
|
|
152
|
-
if not self._ca:
|
|
153
|
-
return MayBe()
|
|
154
|
-
return MayBe(self._ca.foldl(f, start))
|
|
155
|
-
|
|
156
|
-
def foldr[R](self, f: Callable[[D, R], R], start: R | None = None) -> MayBe[R]:
|
|
157
|
-
"""Reduces DEQueue right to left.
|
|
158
|
-
|
|
159
|
-
:param f: reducing function, second argument is for accumulator
|
|
160
|
-
:param start: optional starting value
|
|
161
|
-
:return: MayBe of reduced value with f, empty MayBe if queue empty and no starting value given
|
|
162
|
-
|
|
163
|
-
"""
|
|
164
|
-
if start is None:
|
|
165
|
-
if not self._ca:
|
|
166
|
-
return MayBe()
|
|
167
|
-
return MayBe(self._ca.foldr(f, start))
|
|
168
|
-
|
|
169
|
-
def map[U](self, f: Callable[[D], U]) -> DEQueue[U]:
|
|
170
|
-
"""Map f over the DEQueue left to right, retain original order.
|
|
171
|
-
|
|
172
|
-
:return: new DEQueue instance
|
|
173
|
-
|
|
174
|
-
"""
|
|
175
|
-
return DEQueue(map(f, self._ca))
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
def de_queue[D](*ds: D) -> DEQueue[D]:
|
|
179
|
-
"""DEQueue factory function."""
|
|
180
|
-
return DEQueue(ds)
|
|
@@ -1,145 +0,0 @@
|
|
|
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
|
-
"""Stateful First-In-First-Out (FIFO) Queue data structure.
|
|
16
|
-
|
|
17
|
-
- O(1) pops
|
|
18
|
-
- O(1) amortized pushes
|
|
19
|
-
- in a Boolean context, true if not empty, false if empty
|
|
20
|
-
- will automatically increase storage capacity when needed
|
|
21
|
-
- neither indexable nor sliceable by design
|
|
22
|
-
- O(1) length determination
|
|
23
|
-
|
|
24
|
-
"""
|
|
25
|
-
from __future__ import annotations
|
|
26
|
-
|
|
27
|
-
from collections.abc import Callable, Iterable, Iterator
|
|
28
|
-
from typing import TypeVar
|
|
29
|
-
from pythonic_fp.circulararray.auto import CA
|
|
30
|
-
from pythonic_fp.fptools.maybe import MayBe
|
|
31
|
-
|
|
32
|
-
__all__ = ['FIFOQueue', 'fifo_queue']
|
|
33
|
-
|
|
34
|
-
D = TypeVar('D')
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
class FIFOQueue[D]:
|
|
38
|
-
|
|
39
|
-
__slots__ = ('_ca',)
|
|
40
|
-
|
|
41
|
-
def __init__(self, *dss: Iterable[D]) -> None:
|
|
42
|
-
"""Initial data in natural FIFO order, newest to oldest.
|
|
43
|
-
|
|
44
|
-
:param dss: takes up to one iterable
|
|
45
|
-
:raises ValueError: if more than 1 iterable is given
|
|
46
|
-
|
|
47
|
-
"""
|
|
48
|
-
if (size := len(dss)) > 1:
|
|
49
|
-
msg = f'FIFOQueue expects at most 1 iterable argument, got {size}'
|
|
50
|
-
raise ValueError(msg)
|
|
51
|
-
self._ca = CA(dss[0]) if size == 1 else CA()
|
|
52
|
-
|
|
53
|
-
def __bool__(self) -> bool:
|
|
54
|
-
return len(self._ca) > 0
|
|
55
|
-
|
|
56
|
-
def __len__(self) -> int:
|
|
57
|
-
return len(self._ca)
|
|
58
|
-
|
|
59
|
-
def __eq__(self, other: object) -> bool:
|
|
60
|
-
if not isinstance(other, FIFOQueue):
|
|
61
|
-
return False
|
|
62
|
-
return self._ca == other._ca
|
|
63
|
-
|
|
64
|
-
def __iter__(self) -> Iterator[D]:
|
|
65
|
-
return iter(list(self._ca))
|
|
66
|
-
|
|
67
|
-
def __repr__(self) -> str:
|
|
68
|
-
if len(self) == 0:
|
|
69
|
-
return 'FIFOQueue()'
|
|
70
|
-
return 'FIFOQueue(' + ', '.join(map(repr, self._ca)) + ')'
|
|
71
|
-
|
|
72
|
-
def __str__(self) -> str:
|
|
73
|
-
return '<< ' + ' < '.join(map(str, self)) + ' <<'
|
|
74
|
-
|
|
75
|
-
def copy(self) -> FIFOQueue[D]:
|
|
76
|
-
"""Shallow copy.
|
|
77
|
-
|
|
78
|
-
:return: shallow copy of the FIFOQueue
|
|
79
|
-
|
|
80
|
-
"""
|
|
81
|
-
return FIFOQueue(self._ca)
|
|
82
|
-
|
|
83
|
-
def push(self, *ds: D) -> None:
|
|
84
|
-
"""Push data onto FIFOQueue.
|
|
85
|
-
|
|
86
|
-
:param ds: data items to be pushed onto FIFOQueue
|
|
87
|
-
|
|
88
|
-
"""
|
|
89
|
-
self._ca.pushr(*ds)
|
|
90
|
-
|
|
91
|
-
def pop(self) -> MayBe[D]:
|
|
92
|
-
"""Pop oldest data item off of FIFOQueue.
|
|
93
|
-
|
|
94
|
-
:return: MayBe of popped data item if queue was not empty, empty MayBe otherwise
|
|
95
|
-
|
|
96
|
-
"""
|
|
97
|
-
if self._ca:
|
|
98
|
-
return MayBe(self._ca.popl())
|
|
99
|
-
return MayBe()
|
|
100
|
-
|
|
101
|
-
def peak_last_in(self) -> MayBe[D]:
|
|
102
|
-
"""Peak at newest data item on queue.
|
|
103
|
-
|
|
104
|
-
:return: MayBe of newest data item on queue, empty MayBe if queue empty
|
|
105
|
-
|
|
106
|
-
"""
|
|
107
|
-
if self._ca:
|
|
108
|
-
return MayBe(self._ca[-1])
|
|
109
|
-
return MayBe()
|
|
110
|
-
|
|
111
|
-
def peak_next_out(self) -> MayBe[D]:
|
|
112
|
-
"""Peak at oldest data item on queue.
|
|
113
|
-
|
|
114
|
-
:return: MayBe of oldest data item on queue, empty MayBe if queue empty
|
|
115
|
-
|
|
116
|
-
"""
|
|
117
|
-
if self._ca:
|
|
118
|
-
return MayBe(self._ca[0])
|
|
119
|
-
return MayBe()
|
|
120
|
-
|
|
121
|
-
def fold[T](self, f: Callable[[T, D], T], start: T | None = None) -> MayBe[T]:
|
|
122
|
-
"""Reduces FIFOQueue in natural FIFO Order, oldest to newest.
|
|
123
|
-
|
|
124
|
-
:param f: reducing function, first argument is for accumulator
|
|
125
|
-
:param start: optional starting value
|
|
126
|
-
:return: MayBe of reduced value with f, empty MayBe if queue empty and no starting value given
|
|
127
|
-
|
|
128
|
-
"""
|
|
129
|
-
if start is None:
|
|
130
|
-
if not self._ca:
|
|
131
|
-
return MayBe()
|
|
132
|
-
return MayBe(self._ca.foldl(f, start))
|
|
133
|
-
|
|
134
|
-
def map[U](self, f: Callable[[D], U]) -> FIFOQueue[U]:
|
|
135
|
-
"""Map f over the FIFOQueue, retain original order.
|
|
136
|
-
|
|
137
|
-
:return: new FIFOQueue instance
|
|
138
|
-
|
|
139
|
-
"""
|
|
140
|
-
return FIFOQueue(map(f, self._ca))
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
def fifo_queue[D](*ds: D) -> FIFOQueue[D]:
|
|
144
|
-
"""FIFOQueue factory function."""
|
|
145
|
-
return FIFOQueue(ds)
|
|
@@ -1,136 +0,0 @@
|
|
|
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
|
-
"""Stateful Last-In-First-Out (LIFO) Queue data structure.
|
|
16
|
-
|
|
17
|
-
- O(1) pops
|
|
18
|
-
- O(1) amortized pushes
|
|
19
|
-
- in a Boolean context, true if not empty, false if empty
|
|
20
|
-
- will automatically increase storage capacity when needed
|
|
21
|
-
- neither indexable nor sliceable by design
|
|
22
|
-
- O(1) length determination
|
|
23
|
-
|
|
24
|
-
"""
|
|
25
|
-
from __future__ import annotations
|
|
26
|
-
|
|
27
|
-
from collections.abc import Callable, Iterable, Iterator
|
|
28
|
-
from typing import TypeVar
|
|
29
|
-
from pythonic_fp.circulararray.auto import CA
|
|
30
|
-
from pythonic_fp.fptools.function import swap
|
|
31
|
-
from pythonic_fp.fptools.maybe import MayBe
|
|
32
|
-
|
|
33
|
-
__all__ = ['LIFOQueue', 'lifo_queue']
|
|
34
|
-
|
|
35
|
-
D = TypeVar('D')
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
class LIFOQueue[D]:
|
|
39
|
-
|
|
40
|
-
__slots__ = ('_ca',)
|
|
41
|
-
|
|
42
|
-
def __init__(self, *dss: Iterable[D]) -> None:
|
|
43
|
-
"""Initial data in natural LIFO order, oldest to newest.
|
|
44
|
-
|
|
45
|
-
:param dss: takes up to one iterable
|
|
46
|
-
:raises ValueError: if more than 1 iterable is given
|
|
47
|
-
|
|
48
|
-
"""
|
|
49
|
-
if (size := len(dss)) > 1:
|
|
50
|
-
msg = f'LIFOQueue expects at most 1 iterable argument, got {size}'
|
|
51
|
-
raise ValueError(msg)
|
|
52
|
-
self._ca = CA(dss[0]) if size == 1 else CA()
|
|
53
|
-
|
|
54
|
-
def __bool__(self) -> bool:
|
|
55
|
-
return len(self._ca) > 0
|
|
56
|
-
|
|
57
|
-
def __len__(self) -> int:
|
|
58
|
-
return len(self._ca)
|
|
59
|
-
|
|
60
|
-
def __eq__(self, other: object) -> bool:
|
|
61
|
-
if not isinstance(other, LIFOQueue):
|
|
62
|
-
return False
|
|
63
|
-
return self._ca == other._ca
|
|
64
|
-
|
|
65
|
-
def __iter__(self) -> Iterator[D]:
|
|
66
|
-
return reversed(list(self._ca))
|
|
67
|
-
|
|
68
|
-
def __repr__(self) -> str:
|
|
69
|
-
if len(self) == 0:
|
|
70
|
-
return 'LIFOQueue()'
|
|
71
|
-
return 'LIFOQueue(' + ', '.join(map(repr, self._ca)) + ')'
|
|
72
|
-
|
|
73
|
-
def __str__(self) -> str:
|
|
74
|
-
return '|| ' + ' > '.join(map(str, self)) + ' ><'
|
|
75
|
-
|
|
76
|
-
def copy(self) -> LIFOQueue[D]:
|
|
77
|
-
"""Shallow copy.
|
|
78
|
-
|
|
79
|
-
:return: shallow copy of the LIFOQueue
|
|
80
|
-
|
|
81
|
-
"""
|
|
82
|
-
return LIFOQueue(reversed(self._ca))
|
|
83
|
-
|
|
84
|
-
def push(self, *ds: D) -> None:
|
|
85
|
-
"""Push data onto LIFOQueue.
|
|
86
|
-
|
|
87
|
-
:param ds: data items to be pushed onto LIFOQueue
|
|
88
|
-
|
|
89
|
-
"""
|
|
90
|
-
self._ca.pushr(*ds)
|
|
91
|
-
|
|
92
|
-
def pop(self) -> MayBe[D]:
|
|
93
|
-
"""Pop newest data item off of LIFOQueue.
|
|
94
|
-
|
|
95
|
-
:return: MayBe of popped data item if queue was not empty, empty MayBe otherwise
|
|
96
|
-
|
|
97
|
-
"""
|
|
98
|
-
if self._ca:
|
|
99
|
-
return MayBe(self._ca.popr())
|
|
100
|
-
return MayBe()
|
|
101
|
-
|
|
102
|
-
def peak(self) -> MayBe[D]:
|
|
103
|
-
"""Peak at newest data item on queue.
|
|
104
|
-
|
|
105
|
-
:return: MayBe of newest data item on queue, empty MayBe if queue empty
|
|
106
|
-
|
|
107
|
-
"""
|
|
108
|
-
if self._ca:
|
|
109
|
-
return MayBe(self._ca[-1])
|
|
110
|
-
return MayBe()
|
|
111
|
-
|
|
112
|
-
def fold[T](self, f: Callable[[T, D], T], start: T | None = None) -> MayBe[T]:
|
|
113
|
-
"""Reduces LIFOQUEUE in natural LIFO Order, newest to oldest.
|
|
114
|
-
|
|
115
|
-
:param f: reducing function, first argument is for accumulator
|
|
116
|
-
:param start: optional starting value
|
|
117
|
-
:return: MayBe of reduced value with f, empty MayBe if queue empty and no starting value given
|
|
118
|
-
|
|
119
|
-
"""
|
|
120
|
-
if start is None:
|
|
121
|
-
if not self._ca:
|
|
122
|
-
return MayBe()
|
|
123
|
-
return MayBe(self._ca.foldr(swap(f), start))
|
|
124
|
-
|
|
125
|
-
def map[U](self, f: Callable[[D], U]) -> LIFOQueue[U]:
|
|
126
|
-
"""Map f over the LIFOQueue, retain original order.
|
|
127
|
-
|
|
128
|
-
:return: new LIFOQueue instance
|
|
129
|
-
|
|
130
|
-
"""
|
|
131
|
-
return LIFOQueue(reversed(CA(map(f, reversed(self._ca)))))
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
def lifo_queue[D](*ds: D) -> LIFOQueue[D]:
|
|
135
|
-
"""LIFOQueue factory function."""
|
|
136
|
-
return LIFOQueue(ds)
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: pythonic-fp-containers
|
|
3
|
-
Version: 3.0.0
|
|
4
|
-
Summary: Package of container like data structures.
|
|
5
|
-
Keywords: boxes,functional_tuple,immutable_list,queue,fifo,lifo,dqueue,dequeue
|
|
6
|
-
Author-email: "Geoffrey R. Scheller" <geoffrey@scheller.com>
|
|
7
|
-
Requires-Python: >=3.12
|
|
8
|
-
Description-Content-Type: text/x-rst
|
|
9
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
-
Classifier: Framework :: Pytest
|
|
11
|
-
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
-
Classifier: Operating System :: OS Independent
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
-
Classifier: Typing :: Typed
|
|
16
|
-
License-File: LICENSE
|
|
17
|
-
Requires-Dist: pythonic-fp-circulararray>= 5.3.0
|
|
18
|
-
Requires-Dist: pythonic-fp-fptools>=5.0.0
|
|
19
|
-
Requires-Dist: pythonic-fp-iterables>=5.0.0
|
|
20
|
-
Requires-Dist: pythonic-fp-singletons>=1.0.0
|
|
21
|
-
Requires-Dist: pytest>=8.4.1 ; extra == "test"
|
|
22
|
-
Requires-Dist: pythonic-fp-splitends>=1.0.0 ; extra == "test"
|
|
23
|
-
Project-URL: Changelog, https://github.com/grscheller/pythonic-fp-containers/blob/main/CHANGELOG.rst
|
|
24
|
-
Project-URL: Documentation, https://grscheller.github.io/pythonic-fp/containers/development/build/html/releases.html
|
|
25
|
-
Project-URL: Homepage, https://github.com/grscheller/pythonic-fp/blob/main/README.md
|
|
26
|
-
Project-URL: Source, https://github.com/grscheller/pythonic-fp-containers
|
|
27
|
-
Provides-Extra: test
|
|
28
|
-
|
|
29
|
-
Pythonic FP - Containers
|
|
30
|
-
========================
|
|
31
|
-
|
|
32
|
-
PyPI project
|
|
33
|
-
`pythonic-fp.containers <https://pypi.org/project/pythonic-fp.containers>`_.
|
|
34
|
-
|
|
35
|
-
Python package of container like data structures.
|
|
36
|
-
|
|
37
|
-
+------------------+------------------------------------------------------+
|
|
38
|
-
| Description | Module |
|
|
39
|
-
+==================+======================================================+
|
|
40
|
-
| Single item box | *module* ``pythonic_fp.containers.box`` |
|
|
41
|
-
| Functional tuple | *module* ``pythonic_fp.containers.functional_tuple`` |
|
|
42
|
-
| Immutable list | *module* ``pythonic_fp.containers.immutable_list`` |
|
|
43
|
-
+------------------+------------------------------------------------------+
|
|
44
|
-
|
|
45
|
-
**DEPRECATION WARNING:** ``pythonic_fp.fptools.box`` as been deprecated.
|
|
46
|
-
Use ``pythonic_fp.box`` instead.
|
|
47
|
-
|
|
48
|
-
This PyPI project is part of of the grscheller
|
|
49
|
-
`pythonic-fp namespace projects <https://github.com/grscheller/pythonic-fp/blob/main/README.md>`_.
|
|
50
|
-
|
|
51
|
-
Documentation
|
|
52
|
-
-------------
|
|
53
|
-
|
|
54
|
-
Documentation for this project is hosted on
|
|
55
|
-
`GitHub Pages
|
|
56
|
-
<https://grscheller.github.io/pythonic-fp/containers/development/build/html>`_.
|
|
57
|
-
|
|
58
|
-
Copyright and License
|
|
59
|
-
---------------------
|
|
60
|
-
|
|
61
|
-
Copyright (c) 2023-2025 Geoffrey R. Scheller. Licensed under the Apache
|
|
62
|
-
License, Version 2.0. See the LICENSE file for details.
|
|
63
|
-
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
pythonic_fp/containers/__init__.py,sha256=N5-djJadhpE6h8bg31AtUrqami8RPjsiUYwlwrPpYnI,1603
|
|
2
|
-
pythonic_fp/containers/functional_tuple.py,sha256=AffGbovvg0WpnImZwzhD47djLps27Ns29eKzFosXOC4,5799
|
|
3
|
-
pythonic_fp/containers/immutable_list.py,sha256=SGfGKRyDyBylrNn9qvpoKog531RKAusRsxhtnVa5Rps,6677
|
|
4
|
-
pythonic_fp/containers/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
pythonic_fp/containers/queues/__init__.py,sha256=GJdzodYt32pADih2UHOiR9U4jfZ6SQSw6PsCYZ2r1wg,1459
|
|
6
|
-
pythonic_fp/containers/queues/de.py,sha256=lBoZE8SRe6__NEFpNiqKhumDtdIVvm7S7HMojEUAL1s,5288
|
|
7
|
-
pythonic_fp/containers/queues/fifo.py,sha256=Q9luMfZzSJzGU3aJwnJ5OIeT6hs-6ieh70b5wYGodI4,4221
|
|
8
|
-
pythonic_fp/containers/queues/lifo.py,sha256=xudvLpEvqv9bl3bCqTiwlC2IDZ9A5kHVl-aIjNAKyeA,4031
|
|
9
|
-
pythonic_fp/containers/queues/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
pythonic_fp_containers-3.0.0.dist-info/licenses/LICENSE,sha256=IJMkVWQUVy-SonK6SMaMotDQ6Zb-lVi2bUTiGp1GUO8,10774
|
|
11
|
-
pythonic_fp_containers-3.0.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
12
|
-
pythonic_fp_containers-3.0.0.dist-info/METADATA,sha256=UeoEZi26JqOUc0AYxcwpr8FrxsNEA7XOu5IMySlm_jk,2672
|
|
13
|
-
pythonic_fp_containers-3.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
{pythonic_fp_containers-3.0.0.dist-info → pythonic_fp_containers-4.0.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|