pythonic-fp.queues 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.
Potentially problematic release.
This version of pythonic-fp.queues might be problematic. Click here for more details.
- pythonic_fp/queues/__init__.py +14 -17
- pythonic_fp/queues/de.py +64 -81
- pythonic_fp/queues/fifo.py +50 -58
- pythonic_fp/queues/lifo.py +46 -63
- {pythonic_fp_queues-3.0.0.dist-info → pythonic_fp_queues-4.0.0.dist-info}/METADATA +8 -7
- pythonic_fp_queues-4.0.0.dist-info/RECORD +9 -0
- pythonic_fp_queues-3.0.0.dist-info/RECORD +0 -9
- {pythonic_fp_queues-3.0.0.dist-info → pythonic_fp_queues-4.0.0.dist-info}/WHEEL +0 -0
- {pythonic_fp_queues-3.0.0.dist-info → pythonic_fp_queues-4.0.0.dist-info}/licenses/LICENSE +0 -0
pythonic_fp/queues/__init__.py
CHANGED
|
@@ -12,30 +12,27 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
These are modeled after Python builtins. The initializers take at most one
|
|
18
|
-
iterable while factory functions stand in for Python syntax.
|
|
19
|
-
|
|
20
|
-
Queue types:
|
|
21
|
-
|
|
22
|
-
- *class* dtools.queues.fifo.FIFOQueue: First-In-First-Out Queue
|
|
23
|
-
- *class* dtools.queues.lifo.LIFOQueue: Last-In-First-Out Queue
|
|
24
|
-
- *class* dtools.queues.de.DEQueue: Double-Ended Queue
|
|
15
|
+
"""
|
|
16
|
+
Data structures implementing queue-like behaviors.
|
|
25
17
|
|
|
26
|
-
|
|
18
|
+
- Three Queue-like data structures:
|
|
27
19
|
|
|
28
|
-
- *
|
|
20
|
+
- *class* **FIFOQueue** - First In First Out Queue
|
|
21
|
+
- *class* **LIFOQueue** - Last In First Out Queue
|
|
22
|
+
- *class* **DEQueue** - Double Ended Queue
|
|
29
23
|
|
|
30
|
-
|
|
24
|
+
- Each with capabilities:
|
|
31
25
|
|
|
32
|
-
-
|
|
26
|
+
- O(1) pushes and pops
|
|
27
|
+
- O(1) length determination
|
|
28
|
+
- in a Boolean context
|
|
33
29
|
|
|
34
|
-
|
|
30
|
+
- true if not empty
|
|
31
|
+
- false if empty
|
|
35
32
|
|
|
36
|
-
-
|
|
33
|
+
- will resize themselves larger when needed
|
|
34
|
+
- not indexable or sliceable by design
|
|
37
35
|
|
|
38
|
-
- from function's arguments pushed on from the right
|
|
39
36
|
"""
|
|
40
37
|
|
|
41
38
|
__author__ = 'Geoffrey R. Scheller'
|
pythonic_fp/queues/de.py
CHANGED
|
@@ -12,15 +12,6 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
"""
|
|
16
|
-
### Double-Ended (DE) Queue
|
|
17
|
-
|
|
18
|
-
- stateful DE 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
|
-
- will resize itself larger as needed
|
|
22
|
-
|
|
23
|
-
"""
|
|
24
15
|
|
|
25
16
|
from __future__ import annotations
|
|
26
17
|
|
|
@@ -28,8 +19,7 @@ from collections.abc import Callable, Iterable, Iterator, Sequence
|
|
|
28
19
|
from typing import Never, overload, TypeVar
|
|
29
20
|
|
|
30
21
|
from pythonic_fp.circulararray import CA
|
|
31
|
-
from pythonic_fp.containers.maybe import MayBe
|
|
32
|
-
|
|
22
|
+
from pythonic_fp.containers.maybe import MayBe
|
|
33
23
|
|
|
34
24
|
__all__ = ['DEQueue', 'de_queue']
|
|
35
25
|
|
|
@@ -37,10 +27,9 @@ D = TypeVar('D')
|
|
|
37
27
|
|
|
38
28
|
|
|
39
29
|
class DEQueue[D]:
|
|
40
|
-
"""
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
- order of initial data retained, as if pushed on from the right
|
|
30
|
+
"""
|
|
31
|
+
Stateful Double-Ended (DEQueue) data structure. Order of initial
|
|
32
|
+
data retained, as if pushed on from the right.
|
|
44
33
|
"""
|
|
45
34
|
L = TypeVar('L')
|
|
46
35
|
R = TypeVar('R')
|
|
@@ -50,11 +39,14 @@ class DEQueue[D]:
|
|
|
50
39
|
U = TypeVar('U')
|
|
51
40
|
|
|
52
41
|
def __init__(self, *dss: Iterable[D]) -> None:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
42
|
+
"""
|
|
43
|
+
:param dss: takes one or no iterables
|
|
44
|
+
:raises ValueError: if more than 1 iterable is given
|
|
45
|
+
"""
|
|
46
|
+
if (size := len(dss)) > 1:
|
|
56
47
|
msg = f'DEQueue expects at most 1 iterable argument, got {size}'
|
|
57
|
-
raise
|
|
48
|
+
raise ValueError(msg)
|
|
49
|
+
self._ca = CA(dss[0]) if size == 1 else CA()
|
|
58
50
|
|
|
59
51
|
def __bool__(self) -> bool:
|
|
60
52
|
return len(self._ca) > 0
|
|
@@ -62,21 +54,22 @@ class DEQueue[D]:
|
|
|
62
54
|
def __len__(self) -> int:
|
|
63
55
|
return len(self._ca)
|
|
64
56
|
|
|
65
|
-
def __eq__(self, other: object
|
|
57
|
+
def __eq__(self, other: object) -> bool:
|
|
66
58
|
if not isinstance(other, DEQueue):
|
|
67
59
|
return False
|
|
68
60
|
return self._ca == other._ca
|
|
69
61
|
|
|
70
62
|
@overload
|
|
71
|
-
def __getitem__(self, idx: int
|
|
63
|
+
def __getitem__(self, idx: int) -> D: ...
|
|
72
64
|
@overload
|
|
73
|
-
def __getitem__(self, idx: slice
|
|
65
|
+
def __getitem__(self, idx: slice) -> Sequence[D]: ...
|
|
74
66
|
|
|
75
|
-
def __getitem__(self, idx: int | slice
|
|
67
|
+
def __getitem__(self, idx: int | slice) -> Never:
|
|
76
68
|
if isinstance(idx, slice):
|
|
77
|
-
msg = '
|
|
69
|
+
msg = 'fptools_fp.queues.DEQueue is not slicable by design'
|
|
78
70
|
raise NotImplementedError(msg)
|
|
79
|
-
|
|
71
|
+
msg = 'fptools_fp.queues.DEQueue is not indexable by design'
|
|
72
|
+
raise NotImplementedError(msg)
|
|
80
73
|
|
|
81
74
|
def __iter__(self) -> Iterator[D]:
|
|
82
75
|
return iter(list(self._ca))
|
|
@@ -86,105 +79,95 @@ class DEQueue[D]:
|
|
|
86
79
|
|
|
87
80
|
def __repr__(self) -> str:
|
|
88
81
|
if len(self) == 0:
|
|
89
|
-
return '
|
|
90
|
-
return '
|
|
82
|
+
return 'DEQueue()'
|
|
83
|
+
return 'DEQueue(' + ', '.join(map(repr, self._ca)) + ')'
|
|
91
84
|
|
|
92
85
|
def __str__(self) -> str:
|
|
93
86
|
return '>< ' + ' | '.join(map(str, self)) + ' ><'
|
|
94
87
|
|
|
95
88
|
def copy(self) -> DEQueue[D]:
|
|
96
|
-
"""
|
|
89
|
+
"""Copy.
|
|
90
|
+
|
|
91
|
+
:returns: shallow copy of the DEQueue
|
|
92
|
+
"""
|
|
97
93
|
return DEQueue(self._ca)
|
|
98
94
|
|
|
99
95
|
def pushl(self, *ds: D) -> None:
|
|
100
|
-
"""Push
|
|
96
|
+
"""Push items onto left side of DEQueue."""
|
|
101
97
|
self._ca.pushl(*ds)
|
|
102
98
|
|
|
103
99
|
def pushr(self, *ds: D) -> None:
|
|
104
|
-
"""Push
|
|
105
|
-
Like a Python List, does not return a value.
|
|
106
|
-
"""
|
|
100
|
+
"""Push items onto right side of DEQueue."""
|
|
107
101
|
self._ca.pushr(*ds)
|
|
108
102
|
|
|
109
|
-
def popl(self) ->
|
|
110
|
-
"""Pop
|
|
103
|
+
def popl(self) -> MayBe[D]:
|
|
104
|
+
"""Pop next item from left side DEQueue.
|
|
111
105
|
|
|
112
|
-
|
|
113
|
-
- returns an empty ``MB()`` if queue is empty
|
|
106
|
+
:returns: MayBe of next item popped off left side of queue
|
|
114
107
|
"""
|
|
115
108
|
if self._ca:
|
|
116
|
-
return
|
|
117
|
-
return
|
|
109
|
+
return MayBe(self._ca.popl())
|
|
110
|
+
return MayBe()
|
|
118
111
|
|
|
119
|
-
def popr(self) ->
|
|
120
|
-
"""Pop
|
|
112
|
+
def popr(self) -> MayBe[D]:
|
|
113
|
+
"""Pop next item off right side DEQueue.
|
|
121
114
|
|
|
122
|
-
|
|
123
|
-
- returns an empty ``MB()`` if queue is empty
|
|
115
|
+
:returns: MayBe of item popped off right side of queue
|
|
124
116
|
"""
|
|
125
117
|
if self._ca:
|
|
126
|
-
return
|
|
127
|
-
return
|
|
118
|
+
return MayBe(self._ca.popr())
|
|
119
|
+
return MayBe()
|
|
128
120
|
|
|
129
|
-
def peakl(self) ->
|
|
130
|
-
"""Peak left side of
|
|
121
|
+
def peakl(self) -> MayBe[D]:
|
|
122
|
+
"""Peak at left side of DEQueue. Does not consume item.
|
|
131
123
|
|
|
132
|
-
|
|
133
|
-
- does not consume the item
|
|
134
|
-
- returns an empty ``MB()`` if queue is empty
|
|
124
|
+
:returns: MayBe of first item on left
|
|
135
125
|
"""
|
|
136
126
|
if self._ca:
|
|
137
|
-
return
|
|
138
|
-
return
|
|
127
|
+
return MayBe(self._ca[0])
|
|
128
|
+
return MayBe()
|
|
139
129
|
|
|
140
|
-
def peakr(self) ->
|
|
141
|
-
"""Peak right side of
|
|
130
|
+
def peakr(self) -> MayBe[D]:
|
|
131
|
+
"""Peak at right side of DEQueue. Does not consume item.
|
|
142
132
|
|
|
143
|
-
|
|
144
|
-
- does not consume the item
|
|
145
|
-
- returns an empty ``MB()`` if queue is empty
|
|
133
|
+
:returns: MayBe of last item on right
|
|
146
134
|
"""
|
|
147
135
|
if self._ca:
|
|
148
|
-
return
|
|
149
|
-
return
|
|
136
|
+
return MayBe(self._ca[-1])
|
|
137
|
+
return MayBe()
|
|
150
138
|
|
|
151
|
-
def foldl[L](self, f: Callable[[L, D], L], initial: L | None = None
|
|
152
|
-
"""
|
|
139
|
+
def foldl[L](self, f: Callable[[L, D], L], initial: L | None = None) -> MayBe[L]:
|
|
140
|
+
"""Reduces DEQueue left to right.
|
|
153
141
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
142
|
+
:param f: reducing function, first argument is for accumulator
|
|
143
|
+
:param initial: optional initial value
|
|
144
|
+
:returns: MayBe of reduced value with f
|
|
157
145
|
"""
|
|
158
146
|
if initial is None:
|
|
159
147
|
if not self._ca:
|
|
160
|
-
return
|
|
161
|
-
return
|
|
148
|
+
return MayBe()
|
|
149
|
+
return MayBe(self._ca.foldl(f, initial))
|
|
162
150
|
|
|
163
|
-
def foldr[R](self, f: Callable[[D, R], R], initial: R | None = None
|
|
164
|
-
"""
|
|
151
|
+
def foldr[R](self, f: Callable[[D, R], R], initial: R | None = None) -> MayBe[R]:
|
|
152
|
+
"""Reduces DEQueue right to left.
|
|
165
153
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
154
|
+
:param f: reducing function, second argument is for accumulator
|
|
155
|
+
:param initial: optional initial value
|
|
156
|
+
:returns: MayBe of reduced value with f
|
|
169
157
|
"""
|
|
170
158
|
if initial is None:
|
|
171
159
|
if not self._ca:
|
|
172
|
-
return
|
|
173
|
-
return
|
|
174
|
-
|
|
175
|
-
def map[U](self, f: Callable[[D], U], /) -> DEQueue[U]:
|
|
176
|
-
"""Map a function over ``DEQueue``.
|
|
177
|
-
|
|
178
|
-
- map the function ``f`` over the ``DEQueue``
|
|
160
|
+
return MayBe()
|
|
161
|
+
return MayBe(self._ca.foldr(f, initial))
|
|
179
162
|
|
|
180
|
-
|
|
181
|
-
|
|
163
|
+
def map[U](self, f: Callable[[D], U]) -> DEQueue[U]:
|
|
164
|
+
"""Map f over the DEQueue left to right, retain original order.
|
|
182
165
|
|
|
183
|
-
|
|
166
|
+
:returns: new DEQueue
|
|
184
167
|
"""
|
|
185
168
|
return DEQueue(map(f, self._ca))
|
|
186
169
|
|
|
187
170
|
|
|
188
171
|
def de_queue[D](*ds: D) -> DEQueue[D]:
|
|
189
|
-
"""
|
|
172
|
+
"""DEQueue factory function."""
|
|
190
173
|
return DEQueue(ds)
|
pythonic_fp/queues/fifo.py
CHANGED
|
@@ -12,16 +12,13 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
"""First-In-Last-Out (FIFO) Queue"""
|
|
16
|
-
|
|
17
15
|
from __future__ import annotations
|
|
18
16
|
|
|
19
17
|
from collections.abc import Callable, Iterable, Iterator, Sequence
|
|
20
18
|
from typing import Never, overload, TypeVar
|
|
21
19
|
|
|
22
20
|
from pythonic_fp.circulararray import CA
|
|
23
|
-
from pythonic_fp.containers.maybe import MayBe
|
|
24
|
-
|
|
21
|
+
from pythonic_fp.containers.maybe import MayBe
|
|
25
22
|
|
|
26
23
|
__all__ = ['FIFOQueue', 'fifo_queue']
|
|
27
24
|
|
|
@@ -29,23 +26,24 @@ D = TypeVar('D')
|
|
|
29
26
|
|
|
30
27
|
|
|
31
28
|
class FIFOQueue[D]:
|
|
32
|
-
"""FIFO Queue
|
|
33
|
-
|
|
34
|
-
- stateful First-In-First-Out (FIFO) data structure
|
|
35
|
-
- initial data pushed on in natural FIFO order
|
|
36
29
|
"""
|
|
37
|
-
|
|
30
|
+
Stateful First In First Out (FIFO) data structure. Initial data
|
|
31
|
+
pushed on in natural FIFO order.
|
|
32
|
+
"""
|
|
38
33
|
__slots__ = ('_ca',)
|
|
39
34
|
|
|
40
35
|
T = TypeVar('T')
|
|
41
36
|
U = TypeVar('U')
|
|
42
37
|
|
|
43
38
|
def __init__(self, *dss: Iterable[D]) -> None:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
39
|
+
"""
|
|
40
|
+
:param dss: takes one or no iterables
|
|
41
|
+
:raises ValueError: if more than 1 iterable is given
|
|
42
|
+
"""
|
|
43
|
+
if (size := len(dss)) > 1:
|
|
47
44
|
msg = f'FIFOQueue expects at most 1 iterable argument, got {size}'
|
|
48
45
|
raise ValueError(msg)
|
|
46
|
+
self._ca = CA(dss[0]) if size == 1 else CA()
|
|
49
47
|
|
|
50
48
|
def __bool__(self) -> bool:
|
|
51
49
|
return len(self._ca) > 0
|
|
@@ -53,98 +51,92 @@ class FIFOQueue[D]:
|
|
|
53
51
|
def __len__(self) -> int:
|
|
54
52
|
return len(self._ca)
|
|
55
53
|
|
|
56
|
-
def __eq__(self, other: object
|
|
54
|
+
def __eq__(self, other: object) -> bool:
|
|
57
55
|
if not isinstance(other, FIFOQueue):
|
|
58
56
|
return False
|
|
59
57
|
return self._ca == other._ca
|
|
60
58
|
|
|
61
59
|
@overload
|
|
62
|
-
def __getitem__(self, idx: int
|
|
60
|
+
def __getitem__(self, idx: int) -> D: ...
|
|
63
61
|
@overload
|
|
64
|
-
def __getitem__(self, idx: slice
|
|
62
|
+
def __getitem__(self, idx: slice) -> Sequence[D]: ...
|
|
65
63
|
|
|
66
|
-
def __getitem__(self, idx: int | slice
|
|
64
|
+
def __getitem__(self, idx: int | slice) -> Never:
|
|
67
65
|
if isinstance(idx, slice):
|
|
68
|
-
msg = '
|
|
66
|
+
msg = 'fptools_fp.queues.FIFOQueue is not slicable by design'
|
|
69
67
|
raise NotImplementedError(msg)
|
|
70
|
-
|
|
68
|
+
msg = 'fptools_fp.queues.FIFOQueue is not indexable by design'
|
|
69
|
+
raise NotImplementedError(msg)
|
|
71
70
|
|
|
72
71
|
def __iter__(self) -> Iterator[D]:
|
|
73
72
|
return iter(list(self._ca))
|
|
74
73
|
|
|
75
74
|
def __repr__(self) -> str:
|
|
76
75
|
if len(self) == 0:
|
|
77
|
-
return '
|
|
78
|
-
return '
|
|
76
|
+
return 'FIFOQueue()'
|
|
77
|
+
return 'FIFOQueue(' + ', '.join(map(repr, self._ca)) + ')'
|
|
79
78
|
|
|
80
79
|
def __str__(self) -> str:
|
|
81
80
|
return '<< ' + ' < '.join(map(str, self)) + ' <<'
|
|
82
81
|
|
|
83
82
|
def copy(self) -> FIFOQueue[D]:
|
|
84
|
-
"""
|
|
83
|
+
"""Copy.
|
|
84
|
+
|
|
85
|
+
:returns: shallow copy of the FIFOQueue
|
|
86
|
+
"""
|
|
85
87
|
return FIFOQueue(self._ca)
|
|
86
88
|
|
|
87
89
|
def push(self, *ds: D) -> None:
|
|
88
|
-
"""Push
|
|
90
|
+
"""Push an item onto FIFOQueue."""
|
|
89
91
|
self._ca.pushr(*ds)
|
|
90
92
|
|
|
91
|
-
def pop(self) ->
|
|
92
|
-
"""Pop
|
|
93
|
+
def pop(self) -> MayBe[D]:
|
|
94
|
+
"""Pop oldest item from FIFOQueue.
|
|
93
95
|
|
|
94
|
-
|
|
95
|
-
- returns an empty ``MB()`` if queue is empty
|
|
96
|
+
:returns: MayBe of item popped from queue
|
|
96
97
|
"""
|
|
97
98
|
if self._ca:
|
|
98
|
-
return
|
|
99
|
-
return
|
|
99
|
+
return MayBe(self._ca.popl())
|
|
100
|
+
return MayBe()
|
|
100
101
|
|
|
101
|
-
def peak_last_in(self) ->
|
|
102
|
-
"""Peak
|
|
102
|
+
def peak_last_in(self) -> MayBe[D]:
|
|
103
|
+
"""Peak at end. Does not consume data.
|
|
103
104
|
|
|
104
|
-
|
|
105
|
-
- does not consume the data
|
|
106
|
-
- if item already popped, return ``MB()``
|
|
105
|
+
:returns: MayBe of last item in queue
|
|
107
106
|
"""
|
|
108
107
|
if self._ca:
|
|
109
|
-
return
|
|
110
|
-
return
|
|
108
|
+
return MayBe(self._ca[-1])
|
|
109
|
+
return MayBe()
|
|
111
110
|
|
|
112
|
-
def peak_next_out(self) ->
|
|
113
|
-
"""Peak
|
|
111
|
+
def peak_next_out(self) -> MayBe[D]:
|
|
112
|
+
"""Peak at beginning. Does not consume data.
|
|
114
113
|
|
|
115
|
-
|
|
116
|
-
- does not consume it the item
|
|
117
|
-
- returns ``MB()`` if queue is empty
|
|
114
|
+
:returns: MayBe of next item in queue
|
|
118
115
|
"""
|
|
119
116
|
if self._ca:
|
|
120
|
-
return
|
|
121
|
-
return
|
|
117
|
+
return MayBe(self._ca[0])
|
|
118
|
+
return MayBe()
|
|
122
119
|
|
|
123
|
-
def fold[T](self, f: Callable[[T, D], T], initial: T | None = None
|
|
124
|
-
"""
|
|
120
|
+
def fold[T](self, f: Callable[[T, D], T], initial: T | None = None) -> MayBe[T]:
|
|
121
|
+
"""Reduces in natural FIFO Order (oldest to newest).
|
|
125
122
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
123
|
+
:param f: reducing function, first argument is for accumulator
|
|
124
|
+
:param initial: optional initial value
|
|
125
|
+
:returns: MayBe of reduced value with f
|
|
129
126
|
"""
|
|
130
127
|
if initial is None:
|
|
131
128
|
if not self._ca:
|
|
132
|
-
return
|
|
133
|
-
return
|
|
134
|
-
|
|
135
|
-
def map[U](self, f: Callable[[D], U], /) -> FIFOQueue[U]:
|
|
136
|
-
"""Map over the ``FIFOQueue``.
|
|
137
|
-
|
|
138
|
-
- map function ``f`` over the queue
|
|
129
|
+
return MayBe()
|
|
130
|
+
return MayBe(self._ca.foldl(f, initial))
|
|
139
131
|
|
|
140
|
-
|
|
141
|
-
|
|
132
|
+
def map[U](self, f: Callable[[D], U]) -> FIFOQueue[U]:
|
|
133
|
+
"""Map f over the FIFOQueue, retain original order.
|
|
142
134
|
|
|
143
|
-
|
|
135
|
+
:returns: new FIFOQueue
|
|
144
136
|
"""
|
|
145
137
|
return FIFOQueue(map(f, self._ca))
|
|
146
138
|
|
|
147
139
|
|
|
148
140
|
def fifo_queue[D](*ds: D) -> FIFOQueue[D]:
|
|
149
|
-
"""
|
|
141
|
+
"""FIFOQueue factory function."""
|
|
150
142
|
return FIFOQueue(ds)
|
pythonic_fp/queues/lifo.py
CHANGED
|
@@ -12,23 +12,13 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
"""
|
|
16
|
-
### Last-In-Last-Out (LIFO) Queue
|
|
17
|
-
|
|
18
|
-
- stateful LIFO queue data structures with amortized O(1) pushes and pops
|
|
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
|
-
- will resize itself larger as needed
|
|
22
|
-
|
|
23
|
-
"""
|
|
24
|
-
|
|
25
15
|
from __future__ import annotations
|
|
26
16
|
|
|
27
17
|
from collections.abc import Callable, Iterable, Iterator, Sequence
|
|
28
18
|
from typing import Never, TypeVar, overload
|
|
29
19
|
|
|
30
20
|
from pythonic_fp.circulararray import CA
|
|
31
|
-
from pythonic_fp.containers.maybe import MayBe
|
|
21
|
+
from pythonic_fp.containers.maybe import MayBe
|
|
32
22
|
from pythonic_fp.fptools.function import swap
|
|
33
23
|
|
|
34
24
|
__all__ = ['LIFOQueue', 'lifo_queue']
|
|
@@ -37,11 +27,9 @@ D = TypeVar('D')
|
|
|
37
27
|
|
|
38
28
|
|
|
39
29
|
class LIFOQueue[D]:
|
|
40
|
-
"""
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
- initial data pushed on in natural LIFO order
|
|
44
|
-
|
|
30
|
+
"""
|
|
31
|
+
Stateful Last In First Out (LIFO) data structure. Initial data
|
|
32
|
+
pushed on in natural FIFO order.
|
|
45
33
|
"""
|
|
46
34
|
|
|
47
35
|
__slots__ = ('_ca',)
|
|
@@ -50,11 +38,14 @@ class LIFOQueue[D]:
|
|
|
50
38
|
U = TypeVar('U')
|
|
51
39
|
|
|
52
40
|
def __init__(self, *dss: Iterable[D]) -> None:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
41
|
+
"""
|
|
42
|
+
:param dss: takes one or no iterables
|
|
43
|
+
:raises ValueError: if more than 1 iterable is given
|
|
44
|
+
"""
|
|
45
|
+
if (size := len(dss)) > 1:
|
|
56
46
|
msg = f'LIFOQueue expects at most 1 iterable argument, got {size}'
|
|
57
|
-
raise
|
|
47
|
+
raise ValueError(msg)
|
|
48
|
+
self._ca = CA(dss[0]) if size == 1 else CA()
|
|
58
49
|
|
|
59
50
|
def __bool__(self) -> bool:
|
|
60
51
|
return len(self._ca) > 0
|
|
@@ -62,91 +53,83 @@ class LIFOQueue[D]:
|
|
|
62
53
|
def __len__(self) -> int:
|
|
63
54
|
return len(self._ca)
|
|
64
55
|
|
|
65
|
-
def __eq__(self, other: object
|
|
56
|
+
def __eq__(self, other: object) -> bool:
|
|
66
57
|
if not isinstance(other, LIFOQueue):
|
|
67
58
|
return False
|
|
68
59
|
return self._ca == other._ca
|
|
69
60
|
|
|
70
61
|
@overload
|
|
71
|
-
def __getitem__(self, idx: int
|
|
62
|
+
def __getitem__(self, idx: int) -> D: ...
|
|
72
63
|
@overload
|
|
73
|
-
def __getitem__(self, idx: slice
|
|
64
|
+
def __getitem__(self, idx: slice) -> Sequence[D]: ...
|
|
74
65
|
|
|
75
|
-
def __getitem__(self, idx: int | slice
|
|
66
|
+
def __getitem__(self, idx: int | slice) -> Never:
|
|
76
67
|
if isinstance(idx, slice):
|
|
77
|
-
msg = '
|
|
68
|
+
msg = 'fptools_fp.queues.LIFOQueue is not slicable by design'
|
|
78
69
|
raise NotImplementedError(msg)
|
|
79
|
-
|
|
70
|
+
msg = 'fptools_fp.queues.LIFOQueue is not indexable by design'
|
|
71
|
+
raise NotImplementedError(msg)
|
|
80
72
|
|
|
81
73
|
def __iter__(self) -> Iterator[D]:
|
|
82
74
|
return reversed(list(self._ca))
|
|
83
75
|
|
|
84
76
|
def __repr__(self) -> str:
|
|
85
77
|
if len(self) == 0:
|
|
86
|
-
return '
|
|
87
|
-
return '
|
|
78
|
+
return 'LIFOQueue()'
|
|
79
|
+
return 'LIFOQueue(' + ', '.join(map(repr, self._ca)) + ')'
|
|
88
80
|
|
|
89
81
|
def __str__(self) -> str:
|
|
90
82
|
return '|| ' + ' > '.join(map(str, self)) + ' ><'
|
|
91
83
|
|
|
92
84
|
def copy(self) -> LIFOQueue[D]:
|
|
93
|
-
"""
|
|
85
|
+
"""Copy.
|
|
86
|
+
|
|
87
|
+
:returns: shallow copy of the LIFOQueue
|
|
88
|
+
"""
|
|
94
89
|
return LIFOQueue(reversed(self._ca))
|
|
95
90
|
|
|
96
91
|
def push(self, *ds: D) -> None:
|
|
97
|
-
"""Push
|
|
92
|
+
"""Push an item onto LIFOQueue."""
|
|
98
93
|
self._ca.pushr(*ds)
|
|
99
94
|
|
|
100
|
-
def pop(self) ->
|
|
101
|
-
"""Pop
|
|
102
|
-
|
|
103
|
-
- pop item off of queue, return item in a maybe monad
|
|
104
|
-
- returns an empty ``MB()`` if queue is empty
|
|
95
|
+
def pop(self) -> MayBe[D]:
|
|
96
|
+
"""Pop top item off of LIFOQueue.
|
|
105
97
|
|
|
98
|
+
:returns: MayBe of item popped from queue
|
|
106
99
|
"""
|
|
107
100
|
if self._ca:
|
|
108
|
-
return
|
|
109
|
-
return
|
|
110
|
-
|
|
111
|
-
def peak(self) -> MB[D]:
|
|
112
|
-
"""Peak next data out of ``LIFOQueue``.
|
|
101
|
+
return MayBe(self._ca.popr())
|
|
102
|
+
return MayBe()
|
|
113
103
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
- returns ``MB()`` if queue is empty
|
|
104
|
+
def peak(self) -> MayBe[D]:
|
|
105
|
+
"""Peak lans in/next out. Does not consume data.
|
|
117
106
|
|
|
107
|
+
:returns: MayBe of item at top of queue.
|
|
118
108
|
"""
|
|
119
109
|
if self._ca:
|
|
120
|
-
return
|
|
121
|
-
return
|
|
110
|
+
return MayBe(self._ca[-1])
|
|
111
|
+
return MayBe()
|
|
122
112
|
|
|
123
|
-
def fold[T](self, f: Callable[[T, D], T], initial: T | None = None
|
|
124
|
-
"""
|
|
125
|
-
|
|
126
|
-
- folds in natural LIFO Order (newest to oldest)
|
|
127
|
-
- note that when an initial value is not given then ``~T = ~D``
|
|
128
|
-
- if iterable empty & no initial value given, return ``MB()``
|
|
113
|
+
def fold[T](self, f: Callable[[T, D], T], initial: T | None = None) -> MayBe[T]:
|
|
114
|
+
"""Reduces in natural LIFO Order (newest to oldest.
|
|
129
115
|
|
|
116
|
+
:param f: reducing function, second argument is for accumulator
|
|
117
|
+
:param initial: Optional initial value
|
|
118
|
+
:returns: MayBe of reduced value with f
|
|
130
119
|
"""
|
|
131
120
|
if initial is None:
|
|
132
121
|
if not self._ca:
|
|
133
|
-
return
|
|
134
|
-
return
|
|
135
|
-
|
|
136
|
-
def map[U](self, f: Callable[[D], U], /) -> LIFOQueue[U]:
|
|
137
|
-
"""Map Over the ``LIFOQueue``.
|
|
138
|
-
|
|
139
|
-
- map the function ``f`` over the queue
|
|
140
|
-
|
|
141
|
-
- newest to oldest
|
|
142
|
-
- retain original order
|
|
122
|
+
return MayBe()
|
|
123
|
+
return MayBe(self._ca.foldr(swap(f), initial))
|
|
143
124
|
|
|
144
|
-
|
|
125
|
+
def map[U](self, f: Callable[[D], U]) -> LIFOQueue[U]:
|
|
126
|
+
"""Map f over the LIFOQueue, retain original order.
|
|
145
127
|
|
|
128
|
+
:returns: new LIFOQueue
|
|
146
129
|
"""
|
|
147
130
|
return LIFOQueue(reversed(CA(map(f, reversed(self._ca)))))
|
|
148
131
|
|
|
149
132
|
|
|
150
133
|
def lifo_queue[D](*ds: D) -> LIFOQueue[D]:
|
|
151
|
-
"""
|
|
134
|
+
"""LIFOQueue factory function."""
|
|
152
135
|
return LIFOQueue(ds)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pythonic-fp.queues
|
|
3
|
-
Version:
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 4.0.0
|
|
4
|
+
Summary: Data structures implementing queue-like behaviors.
|
|
5
5
|
Keywords: dqueue,fifo,lifo,queue
|
|
6
6
|
Author-email: "Geoffrey R. Scheller" <geoffrey@scheller.com>
|
|
7
7
|
Requires-Python: >=3.12
|
|
@@ -16,9 +16,9 @@ Classifier: Typing :: Typed
|
|
|
16
16
|
License-File: LICENSE
|
|
17
17
|
Requires-Dist: pythonic-fp.circulararray >= 5.0.0
|
|
18
18
|
Requires-Dist: pythonic-fp.containers >=2.0.0
|
|
19
|
-
Requires-Dist: pythonic-fp.fptools >=
|
|
20
|
-
Project-URL: Changelog, https://
|
|
21
|
-
Project-URL: Documentation, https://grscheller.github.io/pythonic-fp
|
|
19
|
+
Requires-Dist: pythonic-fp.fptools >=4.0.0
|
|
20
|
+
Project-URL: Changelog, https://github.com/grscheller/pythonic-fp-queues/CHANGELOG.rst
|
|
21
|
+
Project-URL: Documentation, https://grscheller.github.io/pythonic-fp/queues/API/development/build/html/
|
|
22
22
|
Project-URL: Homepage, https://github.com/grscheller/pythonic-fp/blob/main/README.md
|
|
23
23
|
Project-URL: Source, https://github.com/grscheller/pythonic-fp-queues
|
|
24
24
|
|
|
@@ -39,8 +39,9 @@ This PyPI project is part of of the grscheller
|
|
|
39
39
|
|
|
40
40
|
## Documentation
|
|
41
41
|
|
|
42
|
-
Documentation
|
|
43
|
-
[GitHub Pages](https://grscheller.github.io/pythonic-fp
|
|
42
|
+
Documentation for package
|
|
43
|
+
[GitHub Pages](https://grscheller.github.io/pythonic-fp/queues/API/development/build/html)
|
|
44
|
+
hosted on GitHub pages.
|
|
44
45
|
|
|
45
46
|
## Copyright and License
|
|
46
47
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
pythonic_fp/queues/__init__.py,sha256=6rZCz04NNvvXxZkXvC3MWgH7hxtu6YCyCnpHDRTL0ek,1215
|
|
2
|
+
pythonic_fp/queues/de.py,sha256=pVzqs2Vd1noU2WbUdCvfDpQd-esqJ5zckOfXBbIKwa8,5193
|
|
3
|
+
pythonic_fp/queues/fifo.py,sha256=dKIdP56PQ-Ssjx9TsOK956Wu5669hSsErtdIc2lydw0,4259
|
|
4
|
+
pythonic_fp/queues/lifo.py,sha256=Pm6ajntvPe0d88UTpB_OZkDEP0WEHBOs71Bye3H2HOg,4116
|
|
5
|
+
pythonic_fp/queues/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
pythonic_fp_queues-4.0.0.dist-info/licenses/LICENSE,sha256=IJMkVWQUVy-SonK6SMaMotDQ6Zb-lVi2bUTiGp1GUO8,10774
|
|
7
|
+
pythonic_fp_queues-4.0.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
8
|
+
pythonic_fp_queues-4.0.0.dist-info/METADATA,sha256=S9diwKGnR_S5zuqJ1U6A9hzQI5dHoHDGqnnABcEmg2g,1927
|
|
9
|
+
pythonic_fp_queues-4.0.0.dist-info/RECORD,,
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
pythonic_fp/queues/__init__.py,sha256=9gxtZ2HyHWVcpcTmTOc_ZNf4-WYGhRMOwhoOf64ulP8,1519
|
|
2
|
-
pythonic_fp/queues/de.py,sha256=d4mgLa5MMPxjzWSEbOhWv1RuJtMwViZR4dxPYKezsUk,5918
|
|
3
|
-
pythonic_fp/queues/fifo.py,sha256=IePckXP722Ky_MKZySawY-q9cFJ6XcMCa_eWXRrbPhI,4519
|
|
4
|
-
pythonic_fp/queues/lifo.py,sha256=LuA3sg5uTMZMXFrf3hTPVDDJ9jbmdKqZNv0zBjcCUrk,4553
|
|
5
|
-
pythonic_fp/queues/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
-
pythonic_fp_queues-3.0.0.dist-info/licenses/LICENSE,sha256=IJMkVWQUVy-SonK6SMaMotDQ6Zb-lVi2bUTiGp1GUO8,10774
|
|
7
|
-
pythonic_fp_queues-3.0.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
8
|
-
pythonic_fp_queues-3.0.0.dist-info/METADATA,sha256=svlf8vGt6_z1PwRAoyYG67cR2HbmxhnYCjkGFMtoSsI,1858
|
|
9
|
-
pythonic_fp_queues-3.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|