dtools.datastructures 0.25.1__py3-none-any.whl → 0.25.2__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.
- dtools/datastructures/__init__.py +9 -9
- dtools/datastructures/nodes.py +31 -23
- dtools/datastructures/queues.py +33 -29
- dtools/datastructures/splitends/__init__.py +3 -3
- dtools/datastructures/splitends/se.py +26 -23
- dtools/datastructures/tuples.py +41 -30
- {dtools_datastructures-0.25.1.dist-info → dtools_datastructures-0.25.2.dist-info}/METADATA +26 -20
- dtools_datastructures-0.25.2.dist-info/RECORD +12 -0
- {dtools_datastructures-0.25.1.dist-info → dtools_datastructures-0.25.2.dist-info}/WHEEL +1 -1
- dtools_datastructures-0.25.1.dist-info/RECORD +0 -12
- {dtools_datastructures-0.25.1.dist-info → dtools_datastructures-0.25.2.dist-info/licenses}/LICENSE +0 -0
@@ -13,20 +13,20 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
15
|
"""
|
16
|
-
###
|
16
|
+
### Developer Tools - data structures for algorithms **DEPRICATED**
|
17
17
|
|
18
18
|
Designed to be helpful when using and implementation algorithms.
|
19
19
|
|
20
20
|
#### Modules and sub-packages
|
21
21
|
|
22
|
-
* module dtools.datastructures.nodes:
|
23
|
-
* package dtools.datastructures.splitends:
|
24
|
-
* module dtools.datastructures.tuples:
|
25
|
-
* module dtools.datastructures.queues:
|
22
|
+
* module dtools.datastructures.nodes: partly incorporated into dtools.splitends
|
23
|
+
* package dtools.datastructures.splitends: now dtools.splitends
|
24
|
+
* module dtools.datastructures.tuples: now dtools.tuples
|
25
|
+
* module dtools.datastructures.queues: now dtools.queues
|
26
26
|
|
27
27
|
"""
|
28
|
-
__version__ = "0.25.1"
|
29
|
-
__author__ = "Geoffrey R. Scheller"
|
30
|
-
__copyright__ = "Copyright (c) 2023-2025 Geoffrey R. Scheller"
|
31
|
-
__license__ = "Apache License 2.0"
|
32
28
|
|
29
|
+
__version__ = '0.25.2'
|
30
|
+
__author__ = 'Geoffrey R. Scheller'
|
31
|
+
__copyright__ = 'Copyright (c) 2023-2025 Geoffrey R. Scheller'
|
32
|
+
__license__ = 'Apache License 2.0'
|
dtools/datastructures/nodes.py
CHANGED
@@ -25,18 +25,20 @@ other data structures which contain these data structures.
|
|
25
25
|
* class **Tree_Node:**
|
26
26
|
|
27
27
|
"""
|
28
|
+
|
28
29
|
from __future__ import annotations
|
29
30
|
from collections.abc import Callable, Iterator
|
30
|
-
from typing import
|
31
|
+
from typing import cast, TypeVar
|
31
32
|
from dtools.fp.err_handling import MB
|
32
33
|
|
33
34
|
__all__ = ['SL_Node', 'DL_Node', 'Tree_Node']
|
34
35
|
|
35
|
-
D = TypeVar('D')
|
36
|
+
D = TypeVar('D') # Not needed for mypy, hint for pdoc.
|
36
37
|
M = TypeVar('M')
|
37
38
|
T = TypeVar('T')
|
38
39
|
|
39
|
-
|
40
|
+
|
41
|
+
class SL_Node[D]:
|
40
42
|
"""Data node for rearward Pointing (tip-to-root) singularly linked graphs.
|
41
43
|
|
42
44
|
* for mutable and immutable linear data structures
|
@@ -52,6 +54,7 @@ class SL_Node[D]():
|
|
52
54
|
* more than one node can point to the same node forming bush like graphs
|
53
55
|
|
54
56
|
"""
|
57
|
+
|
55
58
|
__slots__ = '_data', '_prev'
|
56
59
|
|
57
60
|
def __init__(self, data: D, prev: MB[SL_Node[D]]) -> None:
|
@@ -69,12 +72,14 @@ class SL_Node[D]():
|
|
69
72
|
return self._prev != MB()
|
70
73
|
|
71
74
|
def data_eq(self, other: SL_Node[D]) -> bool:
|
75
|
+
"""Return true if other has same or equal data."""
|
72
76
|
if self._data is other._data:
|
73
77
|
return True
|
74
|
-
|
78
|
+
|
79
|
+
if self._data == other._data:
|
75
80
|
return True
|
76
|
-
|
77
|
-
|
81
|
+
|
82
|
+
return False
|
78
83
|
|
79
84
|
def __eq__(self, other: object) -> bool:
|
80
85
|
if not isinstance(other, type(self)):
|
@@ -82,13 +87,14 @@ class SL_Node[D]():
|
|
82
87
|
|
83
88
|
if self._prev is not other._prev:
|
84
89
|
return False
|
85
|
-
|
86
|
-
|
90
|
+
|
91
|
+
return self.data_eq(other)
|
87
92
|
|
88
93
|
def get_data(self) -> D:
|
94
|
+
"""Return contained data"""
|
89
95
|
return self._data
|
90
96
|
|
91
|
-
def fold[T](self,
|
97
|
+
def fold[T](self, f: Callable[[T, D], T], init: T | None = None) -> T:
|
92
98
|
"""Reduce data across linked nodes.
|
93
99
|
|
94
100
|
* with a function and an optional starting value
|
@@ -117,7 +123,8 @@ class SL_Node[D]():
|
|
117
123
|
"""Push data onto the stack and return a new node containing the data."""
|
118
124
|
return SL_Node(data, MB(self))
|
119
125
|
|
120
|
-
|
126
|
+
|
127
|
+
class DL_Node[D]:
|
121
128
|
"""Doubly Linked Node.
|
122
129
|
|
123
130
|
Doubly linked nodes for graph-like data structures.
|
@@ -129,6 +136,7 @@ class DL_Node[D]():
|
|
129
136
|
* simple recursive binary trees possible
|
130
137
|
|
131
138
|
"""
|
139
|
+
|
132
140
|
__slots__ = '_left', '_data', '_right'
|
133
141
|
|
134
142
|
def __init__(self, left: MB[DL_Node[D]], data: D, right: MB[DL_Node[D]]):
|
@@ -162,33 +170,33 @@ class DL_Node[D]():
|
|
162
170
|
def has_right(self) -> bool:
|
163
171
|
return self._right != MB()
|
164
172
|
|
165
|
-
|
166
|
-
|
173
|
+
|
174
|
+
class Tree_Node[D]:
|
175
|
+
"""Binary Tree Node.
|
167
176
|
|
168
177
|
Nodes useful for binary trees.
|
169
178
|
|
170
179
|
* this type of node always contain data, even if that data is None
|
171
180
|
* in a Boolean context return true if not at the top of the tree
|
172
|
-
* potential uses of metadata can be for re-balancing or repeat counts
|
173
181
|
"""
|
182
|
+
|
174
183
|
__slots__ = '_data', '_left', '_right', '_up'
|
175
184
|
|
176
|
-
def __init__(
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
185
|
+
def __init__(
|
186
|
+
self,
|
187
|
+
data: D,
|
188
|
+
up: MB[Tree_Node[D]],
|
189
|
+
left: MB[Tree_Node[D]],
|
190
|
+
right: MB[Tree_Node[D]]
|
191
|
+
):
|
181
192
|
self._data = data
|
182
193
|
self._up = up
|
183
194
|
self._left = left
|
184
195
|
self._right = right
|
185
196
|
|
186
197
|
def __bool__(self) -> bool:
|
187
|
-
|
188
|
-
return False
|
189
|
-
else:
|
190
|
-
return True
|
198
|
+
return bool(self)
|
191
199
|
|
192
200
|
def is_top(self) -> bool:
|
201
|
+
"""Return true if top node"""
|
193
202
|
return self._up == MB()
|
194
|
-
|
dtools/datastructures/queues.py
CHANGED
@@ -12,7 +12,8 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
"""
|
15
|
+
"""
|
16
|
+
### Queue based data structures
|
16
17
|
|
17
18
|
* stateful queue data structures with amortized O(1) pushes and pops each end
|
18
19
|
* obtaining length (number of elements) of a queue is an O(1) operation
|
@@ -39,6 +40,7 @@
|
|
39
40
|
* function DQ: Constructs a DoubleQueue from a variable number of arguments
|
40
41
|
|
41
42
|
"""
|
43
|
+
|
42
44
|
from __future__ import annotations
|
43
45
|
|
44
46
|
from collections.abc import Callable, Iterable, Iterator, Sequence
|
@@ -46,14 +48,14 @@ from typing import Never, overload, TypeVar
|
|
46
48
|
from dtools.circular_array.ca import ca, CA
|
47
49
|
from dtools.fp.err_handling import MB
|
48
50
|
|
49
|
-
__all__ = [
|
50
|
-
'DQ', 'FQ', 'LQ' ]
|
51
|
+
__all__ = ['DoubleQueue', 'FIFOQueue', 'LIFOQueue', 'QueueBase', 'DQ', 'FQ', 'LQ']
|
51
52
|
|
52
|
-
D = TypeVar('D')
|
53
|
+
D = TypeVar('D') # Not needed for mypy, hint for pdoc.
|
53
54
|
L = TypeVar('L')
|
54
55
|
R = TypeVar('R')
|
55
56
|
U = TypeVar('U')
|
56
57
|
|
58
|
+
|
57
59
|
class QueueBase[D](Sequence[D]):
|
58
60
|
"""Base class for circular area based queues.
|
59
61
|
|
@@ -62,7 +64,8 @@ class QueueBase[D](Sequence[D]):
|
|
62
64
|
* slicing not yet implemented
|
63
65
|
|
64
66
|
"""
|
65
|
-
|
67
|
+
|
68
|
+
__slots__ = ('_ca',)
|
66
69
|
|
67
70
|
def __init__(self, *dss: Iterable[D]) -> None:
|
68
71
|
if len(dss) < 2:
|
@@ -70,7 +73,7 @@ class QueueBase[D](Sequence[D]):
|
|
70
73
|
else:
|
71
74
|
msg1 = f'{type(self).__name__}: expected at most 1 '
|
72
75
|
msg2 = f'iterable argument, got {len(dss)}.'
|
73
|
-
raise TypeError(msg1+msg2)
|
76
|
+
raise TypeError(msg1 + msg2)
|
74
77
|
|
75
78
|
def __bool__(self) -> bool:
|
76
79
|
return len(self._ca) > 0
|
@@ -88,11 +91,12 @@ class QueueBase[D](Sequence[D]):
|
|
88
91
|
@overload
|
89
92
|
def __getitem__(self, idx: slice, /) -> Sequence[D]: ...
|
90
93
|
|
91
|
-
def __getitem__(self, idx: int|slice, /) -> D|Sequence[D]|Never:
|
94
|
+
def __getitem__(self, idx: int | slice, /) -> D | Sequence[D] | Never:
|
92
95
|
if isinstance(idx, slice):
|
93
96
|
raise NotImplementedError
|
94
97
|
return self._ca[idx]
|
95
98
|
|
99
|
+
|
96
100
|
class FIFOQueue[D](QueueBase[D]):
|
97
101
|
"""FIFO Queue
|
98
102
|
|
@@ -100,6 +104,7 @@ class FIFOQueue[D](QueueBase[D]):
|
|
100
104
|
* initial data pushed on in natural FIFO order
|
101
105
|
|
102
106
|
"""
|
107
|
+
|
103
108
|
__slots__ = ()
|
104
109
|
|
105
110
|
def __iter__(self) -> Iterator[D]:
|
@@ -112,7 +117,7 @@ class FIFOQueue[D](QueueBase[D]):
|
|
112
117
|
return 'FQ(' + ', '.join(map(repr, self._ca)) + ')'
|
113
118
|
|
114
119
|
def __str__(self) -> str:
|
115
|
-
return
|
120
|
+
return '<< ' + ' < '.join(map(str, self)) + ' <<'
|
116
121
|
|
117
122
|
def copy(self) -> FIFOQueue[D]:
|
118
123
|
"""Return a shallow copy of the `FIFOQueue`."""
|
@@ -164,7 +169,7 @@ class FIFOQueue[D](QueueBase[D]):
|
|
164
169
|
else:
|
165
170
|
return MB()
|
166
171
|
|
167
|
-
def fold[L](self, f: Callable[[L, D], L], initial: L|None=None, /) -> MB[L]:
|
172
|
+
def fold[L](self, f: Callable[[L, D], L], initial: L | None = None, /) -> MB[L]:
|
168
173
|
"""Fold `FIFOQueue` in natural order.
|
169
174
|
|
170
175
|
Reduce with `f` using an optional initial value.
|
@@ -191,6 +196,7 @@ class FIFOQueue[D](QueueBase[D]):
|
|
191
196
|
"""
|
192
197
|
return FIFOQueue(map(f, self._ca))
|
193
198
|
|
199
|
+
|
194
200
|
class LIFOQueue[D](QueueBase[D]):
|
195
201
|
"""LIFO Queue.
|
196
202
|
|
@@ -198,6 +204,7 @@ class LIFOQueue[D](QueueBase[D]):
|
|
198
204
|
* initial data pushed on in natural LIFO order
|
199
205
|
|
200
206
|
"""
|
207
|
+
|
201
208
|
__slots__ = ()
|
202
209
|
|
203
210
|
def __iter__(self) -> Iterator[D]:
|
@@ -206,11 +213,10 @@ class LIFOQueue[D](QueueBase[D]):
|
|
206
213
|
def __repr__(self) -> str:
|
207
214
|
if len(self) == 0:
|
208
215
|
return 'LQ()'
|
209
|
-
|
210
|
-
return 'LQ(' + ', '.join(map(repr, self._ca)) + ')'
|
216
|
+
return 'LQ(' + ', '.join(map(repr, self._ca)) + ')'
|
211
217
|
|
212
218
|
def __str__(self) -> str:
|
213
|
-
return
|
219
|
+
return '|| ' + ' > '.join(map(str, self)) + ' ><'
|
214
220
|
|
215
221
|
def copy(self) -> LIFOQueue[D]:
|
216
222
|
"""Return a shallow copy of the `LIFOQueue`."""
|
@@ -233,8 +239,7 @@ class LIFOQueue[D](QueueBase[D]):
|
|
233
239
|
"""
|
234
240
|
if self._ca:
|
235
241
|
return MB(self._ca.popR())
|
236
|
-
|
237
|
-
return MB()
|
242
|
+
return MB()
|
238
243
|
|
239
244
|
def peak(self) -> MB[D]:
|
240
245
|
"""Peak next data out of `LIFOQueue`.
|
@@ -246,10 +251,9 @@ class LIFOQueue[D](QueueBase[D]):
|
|
246
251
|
"""
|
247
252
|
if self._ca:
|
248
253
|
return MB(self._ca[-1])
|
249
|
-
|
250
|
-
return MB()
|
254
|
+
return MB()
|
251
255
|
|
252
|
-
def fold[R](self, f: Callable[[D, R], R], initial: R|None=None, /) -> MB[R]:
|
256
|
+
def fold[R](self, f: Callable[[D, R], R], initial: R | None = None, /) -> MB[R]:
|
253
257
|
"""Fold `LIFOQueue` in natural order.
|
254
258
|
|
255
259
|
Reduce with `f` using an optional initial value.
|
@@ -276,6 +280,7 @@ class LIFOQueue[D](QueueBase[D]):
|
|
276
280
|
"""
|
277
281
|
return LIFOQueue(reversed(CA(*map(f, reversed(self._ca)))))
|
278
282
|
|
283
|
+
|
279
284
|
class DoubleQueue[D](QueueBase[D]):
|
280
285
|
"""Double Ended Queue
|
281
286
|
|
@@ -283,6 +288,7 @@ class DoubleQueue[D](QueueBase[D]):
|
|
283
288
|
* order of initial data retained
|
284
289
|
|
285
290
|
"""
|
291
|
+
|
286
292
|
__slots__ = ()
|
287
293
|
|
288
294
|
def __iter__(self) -> Iterator[D]:
|
@@ -294,11 +300,10 @@ class DoubleQueue[D](QueueBase[D]):
|
|
294
300
|
def __repr__(self) -> str:
|
295
301
|
if len(self) == 0:
|
296
302
|
return 'DQ()'
|
297
|
-
|
298
|
-
return 'DQ(' + ', '.join(map(repr, self._ca)) + ')'
|
303
|
+
return 'DQ(' + ', '.join(map(repr, self._ca)) + ')'
|
299
304
|
|
300
305
|
def __str__(self) -> str:
|
301
|
-
return
|
306
|
+
return '>< ' + ' | '.join(map(str, self)) + ' ><'
|
302
307
|
|
303
308
|
def copy(self) -> DoubleQueue[D]:
|
304
309
|
"""Return a shallow copy of the `DoubleQueue`."""
|
@@ -341,8 +346,7 @@ class DoubleQueue[D](QueueBase[D]):
|
|
341
346
|
"""
|
342
347
|
if self._ca:
|
343
348
|
return MB(self._ca.popR())
|
344
|
-
|
345
|
-
return MB()
|
349
|
+
return MB()
|
346
350
|
|
347
351
|
def peakL(self) -> MB[D]:
|
348
352
|
"""Peak left side of `DoubleQueue`.
|
@@ -354,8 +358,7 @@ class DoubleQueue[D](QueueBase[D]):
|
|
354
358
|
"""
|
355
359
|
if self._ca:
|
356
360
|
return MB(self._ca[0])
|
357
|
-
|
358
|
-
return MB()
|
361
|
+
return MB()
|
359
362
|
|
360
363
|
def peakR(self) -> MB[D]:
|
361
364
|
"""Peak right side of `DoubleQueue`.
|
@@ -367,10 +370,9 @@ class DoubleQueue[D](QueueBase[D]):
|
|
367
370
|
"""
|
368
371
|
if self._ca:
|
369
372
|
return MB(self._ca[-1])
|
370
|
-
|
371
|
-
return MB()
|
373
|
+
return MB()
|
372
374
|
|
373
|
-
def foldL[L](self, f: Callable[[L, D], L], initial: L|None=None, /) -> MB[L]:
|
375
|
+
def foldL[L](self, f: Callable[[L, D], L], initial: L | None = None, /) -> MB[L]:
|
374
376
|
"""Fold `DoubleQueue` left to right.
|
375
377
|
|
376
378
|
Reduce left with `f` using an optional initial value.
|
@@ -385,7 +387,7 @@ class DoubleQueue[D](QueueBase[D]):
|
|
385
387
|
return MB()
|
386
388
|
return MB(self._ca.foldL(f, initial=initial))
|
387
389
|
|
388
|
-
def foldR[R](self, f: Callable[[D, R], R], initial: R|None=None, /) -> MB[R]:
|
390
|
+
def foldR[R](self, f: Callable[[D, R], R], initial: R | None = None, /) -> MB[R]:
|
389
391
|
"""Fold `DoubleQueue` right to left.
|
390
392
|
|
391
393
|
Reduce right with `f` using an optional initial value.
|
@@ -411,15 +413,17 @@ class DoubleQueue[D](QueueBase[D]):
|
|
411
413
|
"""
|
412
414
|
return DoubleQueue(map(f, self._ca))
|
413
415
|
|
416
|
+
|
414
417
|
def FQ[D](*ds: D) -> FIFOQueue[D]:
|
415
418
|
"""Return a FIFOQueue where data is pushed on in natural FIFO order."""
|
416
419
|
return FIFOQueue(ds)
|
417
420
|
|
421
|
+
|
418
422
|
def LQ[D](*ds: D) -> LIFOQueue[D]:
|
419
423
|
"""Return a LIFOQueue where data is pushed on in natural LIFO order."""
|
420
424
|
return LIFOQueue(ds)
|
421
425
|
|
426
|
+
|
422
427
|
def DQ[D](*ds: D) -> DoubleQueue[D]:
|
423
428
|
"""Return a DoubleQueue whose data is pushed on from the right."""
|
424
429
|
return DoubleQueue(ds)
|
425
|
-
|
@@ -22,7 +22,7 @@ Singularly linked datastructures that can safely share data between themselves.
|
|
22
22
|
* module **dtools.datastructures.splitends.se: Basic SplitEnd data structure
|
23
23
|
|
24
24
|
"""
|
25
|
-
__author__ = "Geoffrey R. Scheller"
|
26
|
-
__copyright__ = "Copyright (c) 2024-2025 Geoffrey R. Scheller"
|
27
|
-
__license__ = "Apache License 2.0"
|
28
25
|
|
26
|
+
__author__ = 'Geoffrey R. Scheller'
|
27
|
+
__copyright__ = 'Copyright (c) 2024-2025 Geoffrey R. Scheller'
|
28
|
+
__license__ = 'Apache License 2.0'
|
@@ -12,7 +12,8 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
"""
|
15
|
+
"""
|
16
|
+
### SplitEnd stack related data structures
|
16
17
|
|
17
18
|
With use I am finding this data structure needs some sort of supporting
|
18
19
|
infrastructure. Hence I split the original splitend module out to be its own
|
@@ -24,19 +25,21 @@ subpackage.
|
|
24
25
|
* function SE: create SplitEnd from a variable number of arguments
|
25
26
|
|
26
27
|
"""
|
28
|
+
|
27
29
|
from __future__ import annotations
|
28
30
|
|
29
31
|
from collections.abc import Callable, Iterable, Iterator
|
30
|
-
from typing import
|
31
|
-
from ..nodes import SL_Node
|
32
|
+
from typing import Never, TypeVar
|
32
33
|
from dtools.fp.err_handling import MB
|
34
|
+
from ..nodes import SL_Node
|
33
35
|
|
34
|
-
__all__ = [
|
36
|
+
__all__ = ['SplitEnd', 'SE']
|
35
37
|
|
36
38
|
D = TypeVar('D')
|
37
39
|
T = TypeVar('T')
|
38
40
|
|
39
|
-
|
41
|
+
|
42
|
+
class SplitEnd[D]:
|
40
43
|
"""Class SplitEnd
|
41
44
|
|
42
45
|
LIFO stacks which can safely share immutable data between themselves.
|
@@ -51,10 +54,11 @@ class SplitEnd[D]():
|
|
51
54
|
* in boolean context, return true if split end is not empty
|
52
55
|
|
53
56
|
"""
|
57
|
+
|
54
58
|
__slots__ = '_count', '_tip'
|
55
59
|
|
56
60
|
def __init__(self, *dss: Iterable[D]) -> None:
|
57
|
-
if length:=len(dss) < 2:
|
61
|
+
if length := len(dss) < 2:
|
58
62
|
self._tip: MB[SL_Node[D]] = MB()
|
59
63
|
self._count: int = 0
|
60
64
|
if length == 1:
|
@@ -62,7 +66,7 @@ class SplitEnd[D]():
|
|
62
66
|
else:
|
63
67
|
msg1 = 'SplitEnd: expected at most 1 '
|
64
68
|
msg2 = f'iterable argument, got {length}.'
|
65
|
-
raise TypeError(msg1+msg2)
|
69
|
+
raise TypeError(msg1 + msg2)
|
66
70
|
|
67
71
|
def __iter__(self) -> Iterator[D]:
|
68
72
|
if self._tip == MB():
|
@@ -84,7 +88,7 @@ class SplitEnd[D]():
|
|
84
88
|
return 'SE(' + ', '.join(map(repr, reversed(self))) + ')'
|
85
89
|
|
86
90
|
def __str__(self) -> str:
|
87
|
-
return
|
91
|
+
return '>< ' + ' -> '.join(map(str, self)) + ' ||'
|
88
92
|
|
89
93
|
def __eq__(self, other: object, /) -> bool:
|
90
94
|
if not isinstance(other, type(self)):
|
@@ -112,15 +116,15 @@ class SplitEnd[D]():
|
|
112
116
|
"""Push data onto the top of the SplitEnd."""
|
113
117
|
for d in ds:
|
114
118
|
node = SL_Node(d, self._tip)
|
115
|
-
self._tip, self._count = MB(node), self._count+1
|
119
|
+
self._tip, self._count = MB(node), self._count + 1
|
116
120
|
|
117
121
|
def push(self, *ds: D) -> None:
|
118
122
|
"""Push data onto the top of the SplitEnd."""
|
119
123
|
for d in ds:
|
120
124
|
node = SL_Node(d, self._tip)
|
121
|
-
self._tip, self._count = MB(node), self._count+1
|
125
|
+
self._tip, self._count = MB(node), self._count + 1
|
122
126
|
|
123
|
-
def pop(self, default: D|None = None, /) -> D|Never:
|
127
|
+
def pop(self, default: D | None = None, /) -> D | Never:
|
124
128
|
"""Pop data off of the top of the SplitEnd.
|
125
129
|
|
126
130
|
* raises ValueError if
|
@@ -131,13 +135,12 @@ class SplitEnd[D]():
|
|
131
135
|
if self._count == 0:
|
132
136
|
if default is None:
|
133
137
|
raise ValueError('SE: Popping from an empty SplitEnd')
|
134
|
-
|
135
|
-
return default
|
138
|
+
return default
|
136
139
|
|
137
|
-
data, self._tip, self._count = self._tip.get().pop2() + (self._count-1,)
|
140
|
+
data, self._tip, self._count = self._tip.get().pop2() + (self._count - 1,)
|
138
141
|
return data
|
139
142
|
|
140
|
-
def peak(self, default: D|None = None, /) -> D:
|
143
|
+
def peak(self, default: D | None = None, /) -> D:
|
141
144
|
"""Return the data at the top of the SplitEnd.
|
142
145
|
|
143
146
|
* does not consume the data
|
@@ -147,8 +150,7 @@ class SplitEnd[D]():
|
|
147
150
|
if self._count == 0:
|
148
151
|
if default is None:
|
149
152
|
raise ValueError('SE: Popping from an empty SplitEnd')
|
150
|
-
|
151
|
-
return default
|
153
|
+
return default
|
152
154
|
|
153
155
|
return self._tip.get().get_data()
|
154
156
|
|
@@ -163,7 +165,7 @@ class SplitEnd[D]():
|
|
163
165
|
se._tip, se._count = self._tip, self._count
|
164
166
|
return se
|
165
167
|
|
166
|
-
def fold[T](self, f:Callable[[T, D], T], init: T|None = None, /) -> T|Never:
|
168
|
+
def fold[T](self, f: Callable[[T, D], T], init: T | None = None, /) -> T | Never:
|
167
169
|
"""Reduce with a function.
|
168
170
|
|
169
171
|
* folds in natural LIFO Order
|
@@ -171,12 +173,13 @@ class SplitEnd[D]():
|
|
171
173
|
"""
|
172
174
|
if self._tip != MB():
|
173
175
|
return self._tip.get().fold(f, init)
|
174
|
-
|
176
|
+
|
177
|
+
if init is not None:
|
175
178
|
return init
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
+
|
180
|
+
msg = 'SE: Folding empty SplitEnd but no initial value supplied'
|
181
|
+
raise ValueError(msg)
|
182
|
+
|
179
183
|
|
180
184
|
def SE[D](*ds: D) -> SplitEnd[D]:
|
181
185
|
return SplitEnd(ds)
|
182
|
-
|
dtools/datastructures/tuples.py
CHANGED
@@ -12,19 +12,20 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
"""
|
15
|
+
"""
|
16
|
+
### Tuple based data structures
|
16
17
|
|
17
18
|
Only example here is the ftuple, basically an FP interface wrapping a tuple.
|
18
|
-
Originally it inherited from tuple, but I found containing the tuple in a
|
19
|
+
Originally it inherited from tuple, but I found containing the tuple in a
|
19
20
|
"has-a" relationship makes for a faster implementation. Buried in the git
|
20
21
|
history is another example called a "process array" (parray) which I might
|
21
22
|
return to someday. The idea of the parray is a fixed length sequence with
|
22
|
-
sentinel values.
|
23
|
+
sentinel values.
|
23
24
|
|
24
25
|
#### FTuple and FT factory function.
|
25
26
|
|
26
27
|
* class FTuple: Wrapped tuple with a Functional Programming API
|
27
|
-
* function FE:
|
28
|
+
* function FE:
|
28
29
|
|
29
30
|
"""
|
30
31
|
|
@@ -36,12 +37,13 @@ from dtools.fp.iterables import FM, accumulate, concat, exhaust, merge
|
|
36
37
|
|
37
38
|
__all__ = ['FTuple', 'FT']
|
38
39
|
|
39
|
-
D = TypeVar('D')
|
40
|
+
D = TypeVar('D') # Not needed for mypy, hint for pdoc.
|
40
41
|
E = TypeVar('E')
|
41
42
|
L = TypeVar('L')
|
42
|
-
R = TypeVar('
|
43
|
+
R = TypeVar('R')
|
43
44
|
U = TypeVar('U')
|
44
45
|
|
46
|
+
|
45
47
|
class FTuple[D](Sequence[D]):
|
46
48
|
"""
|
47
49
|
#### Functional Tuple
|
@@ -53,7 +55,8 @@ class FTuple[D](Sequence[D]):
|
|
53
55
|
* both left and right int multiplication supported
|
54
56
|
|
55
57
|
"""
|
56
|
-
|
58
|
+
|
59
|
+
__slots__ = ('_ds',)
|
57
60
|
|
58
61
|
def __init__(self, *dss: Iterable[D]) -> None:
|
59
62
|
if len(dss) < 2:
|
@@ -69,7 +72,7 @@ class FTuple[D](Sequence[D]):
|
|
69
72
|
return reversed(self._ds)
|
70
73
|
|
71
74
|
def __bool__(self) -> bool:
|
72
|
-
return bool(
|
75
|
+
return bool(self._ds)
|
73
76
|
|
74
77
|
def __len__(self) -> int:
|
75
78
|
return len(self._ds)
|
@@ -78,7 +81,7 @@ class FTuple[D](Sequence[D]):
|
|
78
81
|
return 'FT(' + ', '.join(map(repr, self)) + ')'
|
79
82
|
|
80
83
|
def __str__(self) -> str:
|
81
|
-
return
|
84
|
+
return '((' + ', '.join(map(repr, self)) + '))'
|
82
85
|
|
83
86
|
def __eq__(self, other: object, /) -> bool:
|
84
87
|
if self is other:
|
@@ -92,16 +95,18 @@ class FTuple[D](Sequence[D]):
|
|
92
95
|
@overload
|
93
96
|
def __getitem__(self, idx: slice, /) -> FTuple[D]: ...
|
94
97
|
|
95
|
-
def __getitem__(self, idx: slice|int, /) -> FTuple[D]|D:
|
98
|
+
def __getitem__(self, idx: slice | int, /) -> FTuple[D] | D:
|
96
99
|
if isinstance(idx, slice):
|
97
100
|
return FTuple(self._ds[idx])
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
101
|
+
return self._ds[idx]
|
102
|
+
|
103
|
+
def foldL[L](
|
104
|
+
self,
|
105
|
+
f: Callable[[L, D], L],
|
106
|
+
/,
|
107
|
+
start: L | None = None,
|
108
|
+
default: L | None = None,
|
109
|
+
) -> L | None:
|
105
110
|
"""
|
106
111
|
**Fold Left**
|
107
112
|
|
@@ -124,10 +129,13 @@ class FTuple[D](Sequence[D]):
|
|
124
129
|
acc = f(acc, v)
|
125
130
|
return acc
|
126
131
|
|
127
|
-
def foldR[R](
|
128
|
-
|
129
|
-
|
130
|
-
|
132
|
+
def foldR[R](
|
133
|
+
self,
|
134
|
+
f: Callable[[D, R], R],
|
135
|
+
/,
|
136
|
+
start: R | None = None,
|
137
|
+
default: R | None = None,
|
138
|
+
) -> R | None:
|
131
139
|
"""
|
132
140
|
**Fold Right**
|
133
141
|
|
@@ -159,7 +167,7 @@ class FTuple[D](Sequence[D]):
|
|
159
167
|
"""
|
160
168
|
return FTuple(self)
|
161
169
|
|
162
|
-
def __add__[E](self, other: FTuple[E], /) -> FTuple[D|E]:
|
170
|
+
def __add__[E](self, other: FTuple[E], /) -> FTuple[D | E]:
|
163
171
|
return FTuple(concat(self, other))
|
164
172
|
|
165
173
|
def __mul__(self, num: int, /) -> FTuple[D]:
|
@@ -168,7 +176,9 @@ class FTuple[D](Sequence[D]):
|
|
168
176
|
def __rmul__(self, num: int, /) -> FTuple[D]:
|
169
177
|
return FTuple(self._ds.__mul__(num if num > 0 else 0))
|
170
178
|
|
171
|
-
def accummulate[L](
|
179
|
+
def accummulate[L](
|
180
|
+
self, f: Callable[[L, D], L], s: L | None = None, /
|
181
|
+
) -> FTuple[L]:
|
172
182
|
"""
|
173
183
|
**Accumulate partial folds**
|
174
184
|
|
@@ -178,13 +188,14 @@ class FTuple[D](Sequence[D]):
|
|
178
188
|
"""
|
179
189
|
if s is None:
|
180
190
|
return FTuple(accumulate(self, f))
|
181
|
-
|
182
|
-
return FTuple(accumulate(self, f, s))
|
191
|
+
return FTuple(accumulate(self, f, s))
|
183
192
|
|
184
193
|
def map[U](self, f: Callable[[D], U], /) -> FTuple[U]:
|
185
194
|
return FTuple(map(f, self))
|
186
195
|
|
187
|
-
def bind[U](
|
196
|
+
def bind[U](
|
197
|
+
self, f: Callable[[D], FTuple[U]], type: FM = FM.CONCAT, /
|
198
|
+
) -> FTuple[U]:
|
188
199
|
"""
|
189
200
|
Bind function `f` to the `FTuple`.
|
190
201
|
|
@@ -195,15 +206,15 @@ class FTuple[D](Sequence[D]):
|
|
195
206
|
"""
|
196
207
|
match type:
|
197
208
|
case FM.CONCAT:
|
198
|
-
return FTuple(concat(*map(
|
209
|
+
return FTuple(concat(*map(f, self)))
|
199
210
|
case FM.MERGE:
|
200
|
-
return FTuple(merge(*map(
|
211
|
+
return FTuple(merge(*map(f, self)))
|
201
212
|
case FM.EXHAUST:
|
202
|
-
return FTuple(exhaust(*map(
|
213
|
+
return FTuple(exhaust(*map(f, self)))
|
203
214
|
case '*':
|
204
215
|
raise ValueError('Unknown FM type')
|
205
216
|
|
217
|
+
|
206
218
|
def FT[D](*ds: D) -> FTuple[D]:
|
207
219
|
"""Return an FTuple whose values are the function arguments."""
|
208
220
|
return FTuple(ds)
|
209
|
-
|
@@ -1,18 +1,19 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: dtools.datastructures
|
3
|
-
Version: 0.25.
|
4
|
-
Summary: ###
|
3
|
+
Version: 0.25.2
|
4
|
+
Summary: ### Developer Tools - data structures for algorithms **DEPRICATED**
|
5
5
|
Keywords: datastructures,data structures,fifo,lifo,stack,queue,SplitEnd
|
6
6
|
Author-email: "Geoffrey R. Scheller" <geoffrey@scheller.com>
|
7
7
|
Requires-Python: >=3.12
|
8
8
|
Description-Content-Type: text/markdown
|
9
|
-
Classifier: Development Status ::
|
9
|
+
Classifier: Development Status :: 7 - Inactive
|
10
10
|
Classifier: Framework :: Pytest
|
11
11
|
Classifier: Intended Audience :: Developers
|
12
12
|
Classifier: License :: OSI Approved :: Apache Software License
|
13
13
|
Classifier: Operating System :: OS Independent
|
14
14
|
Classifier: Programming Language :: Python :: 3.13
|
15
15
|
Classifier: Typing :: Typed
|
16
|
+
License-File: LICENSE
|
16
17
|
Requires-Dist: dtools.circular-array >= 3.9.0, < 3.10
|
17
18
|
Requires-Dist: dtools.fp >= 1.4.0, < 1.5
|
18
19
|
Requires-Dist: pytest >=8.3.2 ; extra == "test"
|
@@ -21,21 +22,27 @@ Project-URL: Documentation, https://grscheller.github.io/dtools-docs/datastructu
|
|
21
22
|
Project-URL: Source, https://github.com/grscheller/dtools-datastructures
|
22
23
|
Provides-Extra: test
|
23
24
|
|
24
|
-
# Developer Tools -
|
25
|
+
# Developer Tools - data structures useful for algorithms
|
26
|
+
|
27
|
+
**DEPRECATED** This project was broken up into separate projects.
|
28
|
+
|
29
|
+
* package dtools.datastructures.splitends `->` dtools.splitends
|
30
|
+
* module dtools.datastructures.tuples `->` dtools.tuples
|
31
|
+
* module dtools.datastructures.queues `->` dtools.queues
|
25
32
|
|
26
33
|
Python package of data structures which support the use and
|
27
34
|
implementation of algorithms.
|
28
35
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
36
|
+
- **Repositories**
|
37
|
+
- [dtools.datastructures][1] project on *PyPI*
|
38
|
+
- [Source code][2] on *GitHub*
|
39
|
+
- **Detailed documentation**
|
40
|
+
- [Detailed API documentation][3] on *GH-Pages*
|
34
41
|
|
35
42
|
This project is part of the
|
36
43
|
[Developer Tools for Python][4] **dtools.** namespace project.
|
37
44
|
|
38
|
-
|
45
|
+
## Overview
|
39
46
|
|
40
47
|
Data structures allowing developers to focus on the algorithms they are
|
41
48
|
using instead of all the "bit fiddling" required to implement behaviors,
|
@@ -47,23 +54,22 @@ introducing inordinate complexity. Some of these data structures allow
|
|
47
54
|
data to be safely shared between multiple data structure instances by
|
48
55
|
making shared data immutable and inaccessible to client code.
|
49
56
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
+
- functional & imperative programming styles supported
|
58
|
+
- functional programming encouraged
|
59
|
+
- project endeavors to remain Pythonic
|
60
|
+
- methods which mutate objects don't return anything
|
61
|
+
- like Python lists
|
62
|
+
- in caparisons identity is considered before equality
|
63
|
+
- like Python builtins
|
57
64
|
|
58
65
|
Sometimes the real power of a data structure comes not from what it
|
59
66
|
empowers you to do, but from what it prevents you from doing to
|
60
67
|
yourself.
|
61
68
|
|
62
|
-
|
69
|
+
______________________________________________________________________
|
63
70
|
|
64
71
|
[1]: https://pypi.org/project/dtools.datastructures/
|
65
72
|
[2]: https://github.com/grscheller/dtools-datastructures/
|
66
73
|
[3]: https://grscheller.github.io/dtools-docs/datastructures/
|
67
74
|
[4]: https://github.com/grscheller/dtools-docs
|
68
75
|
|
69
|
-
|
@@ -0,0 +1,12 @@
|
|
1
|
+
dtools/datastructures/__init__.py,sha256=edwJO3bIyCDcMy7ljHgr87gDlJCO8FvnrM0Ca3wH1iA,1181
|
2
|
+
dtools/datastructures/nodes.py,sha256=diz30k76co1DX30jGlQKiR6PdbaUEUgp-Qsgsv0467c,5666
|
3
|
+
dtools/datastructures/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
|
+
dtools/datastructures/queues.py,sha256=DJLvhmW_CKvZu3ghiCm2ud74lUEQsoHcnwFCEJBSk2I,12111
|
5
|
+
dtools/datastructures/tuples.py,sha256=TjGyA2roVZSBozRx9MxQRJ5aYaiLTckYwsesKoVcJ0k,6677
|
6
|
+
dtools/datastructures/splitends/__init__.py,sha256=EvDVQRe7P7utFSTyLcglYxkXLpDP9ccjQzWxz-mcWZ4,946
|
7
|
+
dtools/datastructures/splitends/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
+
dtools/datastructures/splitends/se.py,sha256=XNVfoOfZh3KAQDTg4ZbinPLUyGokFywrfKLEVcmZF4k,5591
|
9
|
+
dtools_datastructures-0.25.2.dist-info/licenses/LICENSE,sha256=csqbZRvA3Nyuav1aszWvswE8CZtaKr-hMjjjcKqms7w,10774
|
10
|
+
dtools_datastructures-0.25.2.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
11
|
+
dtools_datastructures-0.25.2.dist-info/METADATA,sha256=1UgIeHlr9DYBjLkfK2b1XAhKEJw4PQXZm4V2WP5w6T0,3143
|
12
|
+
dtools_datastructures-0.25.2.dist-info/RECORD,,
|
@@ -1,12 +0,0 @@
|
|
1
|
-
dtools/datastructures/__init__.py,sha256=ST-ArKsBdvwM8l48BLTBGr-cljt6ulNw9N4QIRCC2Ck,1164
|
2
|
-
dtools/datastructures/nodes.py,sha256=kbk1rcHhicQZssDguRxybakUi450qrfrHBSrf6blM78,5757
|
3
|
-
dtools/datastructures/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
|
-
dtools/datastructures/queues.py,sha256=BkvGl1Opooozz46mgPNPMgXnI57yXcABAP9lwXKk8_M,12173
|
5
|
-
dtools/datastructures/tuples.py,sha256=uc1kU87rf6L9sOaBVRMKP8ZT1roACIm2pJUBaF_M5-k,6678
|
6
|
-
dtools/datastructures/splitends/__init__.py,sha256=y9zPBG_ZM_l0uDWZr4Xown72VnR3dvqlhrvTybLP09c,946
|
7
|
-
dtools/datastructures/splitends/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
-
dtools/datastructures/splitends/se.py,sha256=M0lsaLVmb_zbt8CvXXGd090FUKzuxPgWtRfLvXtls_A,5644
|
9
|
-
dtools_datastructures-0.25.1.dist-info/LICENSE,sha256=csqbZRvA3Nyuav1aszWvswE8CZtaKr-hMjjjcKqms7w,10774
|
10
|
-
dtools_datastructures-0.25.1.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
|
11
|
-
dtools_datastructures-0.25.1.dist-info/METADATA,sha256=pG5tlUXUTkoolVEUtSzSgH_PcZvpgU_eYwmPfcoohGg,2764
|
12
|
-
dtools_datastructures-0.25.1.dist-info/RECORD,,
|
{dtools_datastructures-0.25.1.dist-info → dtools_datastructures-0.25.2.dist-info/licenses}/LICENSE
RENAMED
File without changes
|