dtools.datastructures 0.25.0__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 +10 -9
- dtools/datastructures/nodes.py +34 -21
- dtools/datastructures/queues.py +44 -35
- dtools/datastructures/splitends/__init__.py +5 -4
- dtools/datastructures/splitends/se.py +29 -23
- dtools/datastructures/tuples.py +46 -29
- {dtools_datastructures-0.25.0.dist-info → dtools_datastructures-0.25.2.dist-info}/METADATA +31 -21
- dtools_datastructures-0.25.2.dist-info/RECORD +12 -0
- {dtools_datastructures-0.25.0.dist-info → dtools_datastructures-0.25.2.dist-info}/WHEEL +1 -1
- dtools_datastructures-0.25.0.dist-info/RECORD +0 -12
- {dtools_datastructures-0.25.0.dist-info → dtools_datastructures-0.25.2.dist-info/licenses}/LICENSE +0 -0
@@ -13,19 +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
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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,14 +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
|
-
|
36
|
+
D = TypeVar('D') # Not needed for mypy, hint for pdoc.
|
37
|
+
M = TypeVar('M')
|
38
|
+
T = TypeVar('T')
|
39
|
+
|
40
|
+
|
41
|
+
class SL_Node[D]:
|
36
42
|
"""Data node for rearward Pointing (tip-to-root) singularly linked graphs.
|
37
43
|
|
38
44
|
* for mutable and immutable linear data structures
|
@@ -48,6 +54,7 @@ class SL_Node[D]():
|
|
48
54
|
* more than one node can point to the same node forming bush like graphs
|
49
55
|
|
50
56
|
"""
|
57
|
+
|
51
58
|
__slots__ = '_data', '_prev'
|
52
59
|
|
53
60
|
def __init__(self, data: D, prev: MB[SL_Node[D]]) -> None:
|
@@ -65,12 +72,14 @@ class SL_Node[D]():
|
|
65
72
|
return self._prev != MB()
|
66
73
|
|
67
74
|
def data_eq(self, other: SL_Node[D]) -> bool:
|
75
|
+
"""Return true if other has same or equal data."""
|
68
76
|
if self._data is other._data:
|
69
77
|
return True
|
70
|
-
|
78
|
+
|
79
|
+
if self._data == other._data:
|
71
80
|
return True
|
72
|
-
|
73
|
-
|
81
|
+
|
82
|
+
return False
|
74
83
|
|
75
84
|
def __eq__(self, other: object) -> bool:
|
76
85
|
if not isinstance(other, type(self)):
|
@@ -78,13 +87,14 @@ class SL_Node[D]():
|
|
78
87
|
|
79
88
|
if self._prev is not other._prev:
|
80
89
|
return False
|
81
|
-
|
82
|
-
|
90
|
+
|
91
|
+
return self.data_eq(other)
|
83
92
|
|
84
93
|
def get_data(self) -> D:
|
94
|
+
"""Return contained data"""
|
85
95
|
return self._data
|
86
96
|
|
87
|
-
def fold[T](self,
|
97
|
+
def fold[T](self, f: Callable[[T, D], T], init: T | None = None) -> T:
|
88
98
|
"""Reduce data across linked nodes.
|
89
99
|
|
90
100
|
* with a function and an optional starting value
|
@@ -113,7 +123,8 @@ class SL_Node[D]():
|
|
113
123
|
"""Push data onto the stack and return a new node containing the data."""
|
114
124
|
return SL_Node(data, MB(self))
|
115
125
|
|
116
|
-
|
126
|
+
|
127
|
+
class DL_Node[D]:
|
117
128
|
"""Doubly Linked Node.
|
118
129
|
|
119
130
|
Doubly linked nodes for graph-like data structures.
|
@@ -125,6 +136,7 @@ class DL_Node[D]():
|
|
125
136
|
* simple recursive binary trees possible
|
126
137
|
|
127
138
|
"""
|
139
|
+
|
128
140
|
__slots__ = '_left', '_data', '_right'
|
129
141
|
|
130
142
|
def __init__(self, left: MB[DL_Node[D]], data: D, right: MB[DL_Node[D]]):
|
@@ -158,32 +170,33 @@ class DL_Node[D]():
|
|
158
170
|
def has_right(self) -> bool:
|
159
171
|
return self._right != MB()
|
160
172
|
|
161
|
-
|
162
|
-
|
173
|
+
|
174
|
+
class Tree_Node[D]:
|
175
|
+
"""Binary Tree Node.
|
163
176
|
|
164
177
|
Nodes useful for binary trees.
|
165
178
|
|
166
179
|
* this type of node always contain data, even if that data is None
|
167
180
|
* in a Boolean context return true if not at the top of the tree
|
168
|
-
* potential uses of metadata can be for re-balancing or repeat counts
|
169
181
|
"""
|
182
|
+
|
170
183
|
__slots__ = '_data', '_left', '_right', '_up'
|
171
184
|
|
172
|
-
def __init__(
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
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
|
+
):
|
177
192
|
self._data = data
|
178
193
|
self._up = up
|
179
194
|
self._left = left
|
180
195
|
self._right = right
|
181
196
|
|
182
197
|
def __bool__(self) -> bool:
|
183
|
-
|
184
|
-
return False
|
185
|
-
else:
|
186
|
-
return True
|
198
|
+
return bool(self)
|
187
199
|
|
188
200
|
def is_top(self) -> bool:
|
201
|
+
"""Return true if top node"""
|
189
202
|
return self._up == MB()
|
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
|
@@ -21,33 +22,39 @@
|
|
21
22
|
|
22
23
|
#### FIFOQueue
|
23
24
|
|
24
|
-
* class
|
25
|
-
* function
|
25
|
+
* class FIFOQueue: First-In-First-Out Queue
|
26
|
+
* function FQ: Constructs a FIFOQueue from a variable number of arguments
|
26
27
|
|
27
28
|
---
|
28
29
|
|
29
30
|
#### LIFOQueue
|
30
31
|
|
31
|
-
* class
|
32
|
-
* function
|
32
|
+
* class LIFOQueue: Last-In-First-Out Queue
|
33
|
+
* function LQ: Constructs a LIFOQueue from a variable number of arguments
|
33
34
|
|
34
35
|
---
|
35
36
|
|
36
37
|
#### DoubleQueue
|
37
38
|
|
38
|
-
* class
|
39
|
-
* function
|
39
|
+
* class DoubleQueue: Double-Ended Queue
|
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
|
45
|
-
from typing import Never, overload
|
47
|
+
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
|
-
|
51
|
+
__all__ = ['DoubleQueue', 'FIFOQueue', 'LIFOQueue', 'QueueBase', 'DQ', 'FQ', 'LQ']
|
52
|
+
|
53
|
+
D = TypeVar('D') # Not needed for mypy, hint for pdoc.
|
54
|
+
L = TypeVar('L')
|
55
|
+
R = TypeVar('R')
|
56
|
+
U = TypeVar('U')
|
57
|
+
|
51
58
|
|
52
59
|
class QueueBase[D](Sequence[D]):
|
53
60
|
"""Base class for circular area based queues.
|
@@ -57,7 +64,8 @@ class QueueBase[D](Sequence[D]):
|
|
57
64
|
* slicing not yet implemented
|
58
65
|
|
59
66
|
"""
|
60
|
-
|
67
|
+
|
68
|
+
__slots__ = ('_ca',)
|
61
69
|
|
62
70
|
def __init__(self, *dss: Iterable[D]) -> None:
|
63
71
|
if len(dss) < 2:
|
@@ -65,7 +73,7 @@ class QueueBase[D](Sequence[D]):
|
|
65
73
|
else:
|
66
74
|
msg1 = f'{type(self).__name__}: expected at most 1 '
|
67
75
|
msg2 = f'iterable argument, got {len(dss)}.'
|
68
|
-
raise TypeError(msg1+msg2)
|
76
|
+
raise TypeError(msg1 + msg2)
|
69
77
|
|
70
78
|
def __bool__(self) -> bool:
|
71
79
|
return len(self._ca) > 0
|
@@ -83,11 +91,12 @@ class QueueBase[D](Sequence[D]):
|
|
83
91
|
@overload
|
84
92
|
def __getitem__(self, idx: slice, /) -> Sequence[D]: ...
|
85
93
|
|
86
|
-
def __getitem__(self, idx: int|slice, /) -> D|Sequence[D]|Never:
|
94
|
+
def __getitem__(self, idx: int | slice, /) -> D | Sequence[D] | Never:
|
87
95
|
if isinstance(idx, slice):
|
88
96
|
raise NotImplementedError
|
89
97
|
return self._ca[idx]
|
90
98
|
|
99
|
+
|
91
100
|
class FIFOQueue[D](QueueBase[D]):
|
92
101
|
"""FIFO Queue
|
93
102
|
|
@@ -95,6 +104,7 @@ class FIFOQueue[D](QueueBase[D]):
|
|
95
104
|
* initial data pushed on in natural FIFO order
|
96
105
|
|
97
106
|
"""
|
107
|
+
|
98
108
|
__slots__ = ()
|
99
109
|
|
100
110
|
def __iter__(self) -> Iterator[D]:
|
@@ -107,7 +117,7 @@ class FIFOQueue[D](QueueBase[D]):
|
|
107
117
|
return 'FQ(' + ', '.join(map(repr, self._ca)) + ')'
|
108
118
|
|
109
119
|
def __str__(self) -> str:
|
110
|
-
return
|
120
|
+
return '<< ' + ' < '.join(map(str, self)) + ' <<'
|
111
121
|
|
112
122
|
def copy(self) -> FIFOQueue[D]:
|
113
123
|
"""Return a shallow copy of the `FIFOQueue`."""
|
@@ -159,7 +169,7 @@ class FIFOQueue[D](QueueBase[D]):
|
|
159
169
|
else:
|
160
170
|
return MB()
|
161
171
|
|
162
|
-
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]:
|
163
173
|
"""Fold `FIFOQueue` in natural order.
|
164
174
|
|
165
175
|
Reduce with `f` using an optional initial value.
|
@@ -186,6 +196,7 @@ class FIFOQueue[D](QueueBase[D]):
|
|
186
196
|
"""
|
187
197
|
return FIFOQueue(map(f, self._ca))
|
188
198
|
|
199
|
+
|
189
200
|
class LIFOQueue[D](QueueBase[D]):
|
190
201
|
"""LIFO Queue.
|
191
202
|
|
@@ -193,6 +204,7 @@ class LIFOQueue[D](QueueBase[D]):
|
|
193
204
|
* initial data pushed on in natural LIFO order
|
194
205
|
|
195
206
|
"""
|
207
|
+
|
196
208
|
__slots__ = ()
|
197
209
|
|
198
210
|
def __iter__(self) -> Iterator[D]:
|
@@ -201,11 +213,10 @@ class LIFOQueue[D](QueueBase[D]):
|
|
201
213
|
def __repr__(self) -> str:
|
202
214
|
if len(self) == 0:
|
203
215
|
return 'LQ()'
|
204
|
-
|
205
|
-
return 'LQ(' + ', '.join(map(repr, self._ca)) + ')'
|
216
|
+
return 'LQ(' + ', '.join(map(repr, self._ca)) + ')'
|
206
217
|
|
207
218
|
def __str__(self) -> str:
|
208
|
-
return
|
219
|
+
return '|| ' + ' > '.join(map(str, self)) + ' ><'
|
209
220
|
|
210
221
|
def copy(self) -> LIFOQueue[D]:
|
211
222
|
"""Return a shallow copy of the `LIFOQueue`."""
|
@@ -228,8 +239,7 @@ class LIFOQueue[D](QueueBase[D]):
|
|
228
239
|
"""
|
229
240
|
if self._ca:
|
230
241
|
return MB(self._ca.popR())
|
231
|
-
|
232
|
-
return MB()
|
242
|
+
return MB()
|
233
243
|
|
234
244
|
def peak(self) -> MB[D]:
|
235
245
|
"""Peak next data out of `LIFOQueue`.
|
@@ -241,10 +251,9 @@ class LIFOQueue[D](QueueBase[D]):
|
|
241
251
|
"""
|
242
252
|
if self._ca:
|
243
253
|
return MB(self._ca[-1])
|
244
|
-
|
245
|
-
return MB()
|
254
|
+
return MB()
|
246
255
|
|
247
|
-
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]:
|
248
257
|
"""Fold `LIFOQueue` in natural order.
|
249
258
|
|
250
259
|
Reduce with `f` using an optional initial value.
|
@@ -271,6 +280,7 @@ class LIFOQueue[D](QueueBase[D]):
|
|
271
280
|
"""
|
272
281
|
return LIFOQueue(reversed(CA(*map(f, reversed(self._ca)))))
|
273
282
|
|
283
|
+
|
274
284
|
class DoubleQueue[D](QueueBase[D]):
|
275
285
|
"""Double Ended Queue
|
276
286
|
|
@@ -278,6 +288,7 @@ class DoubleQueue[D](QueueBase[D]):
|
|
278
288
|
* order of initial data retained
|
279
289
|
|
280
290
|
"""
|
291
|
+
|
281
292
|
__slots__ = ()
|
282
293
|
|
283
294
|
def __iter__(self) -> Iterator[D]:
|
@@ -289,11 +300,10 @@ class DoubleQueue[D](QueueBase[D]):
|
|
289
300
|
def __repr__(self) -> str:
|
290
301
|
if len(self) == 0:
|
291
302
|
return 'DQ()'
|
292
|
-
|
293
|
-
return 'DQ(' + ', '.join(map(repr, self._ca)) + ')'
|
303
|
+
return 'DQ(' + ', '.join(map(repr, self._ca)) + ')'
|
294
304
|
|
295
305
|
def __str__(self) -> str:
|
296
|
-
return
|
306
|
+
return '>< ' + ' | '.join(map(str, self)) + ' ><'
|
297
307
|
|
298
308
|
def copy(self) -> DoubleQueue[D]:
|
299
309
|
"""Return a shallow copy of the `DoubleQueue`."""
|
@@ -336,8 +346,7 @@ class DoubleQueue[D](QueueBase[D]):
|
|
336
346
|
"""
|
337
347
|
if self._ca:
|
338
348
|
return MB(self._ca.popR())
|
339
|
-
|
340
|
-
return MB()
|
349
|
+
return MB()
|
341
350
|
|
342
351
|
def peakL(self) -> MB[D]:
|
343
352
|
"""Peak left side of `DoubleQueue`.
|
@@ -349,8 +358,7 @@ class DoubleQueue[D](QueueBase[D]):
|
|
349
358
|
"""
|
350
359
|
if self._ca:
|
351
360
|
return MB(self._ca[0])
|
352
|
-
|
353
|
-
return MB()
|
361
|
+
return MB()
|
354
362
|
|
355
363
|
def peakR(self) -> MB[D]:
|
356
364
|
"""Peak right side of `DoubleQueue`.
|
@@ -362,10 +370,9 @@ class DoubleQueue[D](QueueBase[D]):
|
|
362
370
|
"""
|
363
371
|
if self._ca:
|
364
372
|
return MB(self._ca[-1])
|
365
|
-
|
366
|
-
return MB()
|
373
|
+
return MB()
|
367
374
|
|
368
|
-
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]:
|
369
376
|
"""Fold `DoubleQueue` left to right.
|
370
377
|
|
371
378
|
Reduce left with `f` using an optional initial value.
|
@@ -380,7 +387,7 @@ class DoubleQueue[D](QueueBase[D]):
|
|
380
387
|
return MB()
|
381
388
|
return MB(self._ca.foldL(f, initial=initial))
|
382
389
|
|
383
|
-
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]:
|
384
391
|
"""Fold `DoubleQueue` right to left.
|
385
392
|
|
386
393
|
Reduce right with `f` using an optional initial value.
|
@@ -406,15 +413,17 @@ class DoubleQueue[D](QueueBase[D]):
|
|
406
413
|
"""
|
407
414
|
return DoubleQueue(map(f, self._ca))
|
408
415
|
|
416
|
+
|
409
417
|
def FQ[D](*ds: D) -> FIFOQueue[D]:
|
410
418
|
"""Return a FIFOQueue where data is pushed on in natural FIFO order."""
|
411
419
|
return FIFOQueue(ds)
|
412
420
|
|
421
|
+
|
413
422
|
def LQ[D](*ds: D) -> LIFOQueue[D]:
|
414
423
|
"""Return a LIFOQueue where data is pushed on in natural LIFO order."""
|
415
424
|
return LIFOQueue(ds)
|
416
425
|
|
426
|
+
|
417
427
|
def DQ[D](*ds: D) -> DoubleQueue[D]:
|
418
428
|
"""Return a DoubleQueue whose data is pushed on from the right."""
|
419
429
|
return DoubleQueue(ds)
|
420
|
-
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright
|
1
|
+
# Copyright 2024-2025 Geoffrey R. Scheller
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -22,6 +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
|
-
|
26
|
-
|
27
|
-
|
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,16 +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
|
35
|
+
|
36
|
+
__all__ = ['SplitEnd', 'SE']
|
37
|
+
|
38
|
+
D = TypeVar('D')
|
39
|
+
T = TypeVar('T')
|
33
40
|
|
34
|
-
__all__ = [ 'SplitEnd', 'SE' ]
|
35
41
|
|
36
|
-
class SplitEnd[D]
|
42
|
+
class SplitEnd[D]:
|
37
43
|
"""Class SplitEnd
|
38
44
|
|
39
45
|
LIFO stacks which can safely share immutable data between themselves.
|
@@ -48,10 +54,11 @@ class SplitEnd[D]():
|
|
48
54
|
* in boolean context, return true if split end is not empty
|
49
55
|
|
50
56
|
"""
|
57
|
+
|
51
58
|
__slots__ = '_count', '_tip'
|
52
59
|
|
53
60
|
def __init__(self, *dss: Iterable[D]) -> None:
|
54
|
-
if length:=len(dss) < 2:
|
61
|
+
if length := len(dss) < 2:
|
55
62
|
self._tip: MB[SL_Node[D]] = MB()
|
56
63
|
self._count: int = 0
|
57
64
|
if length == 1:
|
@@ -59,7 +66,7 @@ class SplitEnd[D]():
|
|
59
66
|
else:
|
60
67
|
msg1 = 'SplitEnd: expected at most 1 '
|
61
68
|
msg2 = f'iterable argument, got {length}.'
|
62
|
-
raise TypeError(msg1+msg2)
|
69
|
+
raise TypeError(msg1 + msg2)
|
63
70
|
|
64
71
|
def __iter__(self) -> Iterator[D]:
|
65
72
|
if self._tip == MB():
|
@@ -81,7 +88,7 @@ class SplitEnd[D]():
|
|
81
88
|
return 'SE(' + ', '.join(map(repr, reversed(self))) + ')'
|
82
89
|
|
83
90
|
def __str__(self) -> str:
|
84
|
-
return
|
91
|
+
return '>< ' + ' -> '.join(map(str, self)) + ' ||'
|
85
92
|
|
86
93
|
def __eq__(self, other: object, /) -> bool:
|
87
94
|
if not isinstance(other, type(self)):
|
@@ -109,15 +116,15 @@ class SplitEnd[D]():
|
|
109
116
|
"""Push data onto the top of the SplitEnd."""
|
110
117
|
for d in ds:
|
111
118
|
node = SL_Node(d, self._tip)
|
112
|
-
self._tip, self._count = MB(node), self._count+1
|
119
|
+
self._tip, self._count = MB(node), self._count + 1
|
113
120
|
|
114
121
|
def push(self, *ds: D) -> None:
|
115
122
|
"""Push data onto the top of the SplitEnd."""
|
116
123
|
for d in ds:
|
117
124
|
node = SL_Node(d, self._tip)
|
118
|
-
self._tip, self._count = MB(node), self._count+1
|
125
|
+
self._tip, self._count = MB(node), self._count + 1
|
119
126
|
|
120
|
-
def pop(self, default: D|None = None, /) -> D|Never:
|
127
|
+
def pop(self, default: D | None = None, /) -> D | Never:
|
121
128
|
"""Pop data off of the top of the SplitEnd.
|
122
129
|
|
123
130
|
* raises ValueError if
|
@@ -128,13 +135,12 @@ class SplitEnd[D]():
|
|
128
135
|
if self._count == 0:
|
129
136
|
if default is None:
|
130
137
|
raise ValueError('SE: Popping from an empty SplitEnd')
|
131
|
-
|
132
|
-
return default
|
138
|
+
return default
|
133
139
|
|
134
|
-
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,)
|
135
141
|
return data
|
136
142
|
|
137
|
-
def peak(self, default: D|None = None, /) -> D:
|
143
|
+
def peak(self, default: D | None = None, /) -> D:
|
138
144
|
"""Return the data at the top of the SplitEnd.
|
139
145
|
|
140
146
|
* does not consume the data
|
@@ -144,8 +150,7 @@ class SplitEnd[D]():
|
|
144
150
|
if self._count == 0:
|
145
151
|
if default is None:
|
146
152
|
raise ValueError('SE: Popping from an empty SplitEnd')
|
147
|
-
|
148
|
-
return default
|
153
|
+
return default
|
149
154
|
|
150
155
|
return self._tip.get().get_data()
|
151
156
|
|
@@ -160,7 +165,7 @@ class SplitEnd[D]():
|
|
160
165
|
se._tip, se._count = self._tip, self._count
|
161
166
|
return se
|
162
167
|
|
163
|
-
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:
|
164
169
|
"""Reduce with a function.
|
165
170
|
|
166
171
|
* folds in natural LIFO Order
|
@@ -168,12 +173,13 @@ class SplitEnd[D]():
|
|
168
173
|
"""
|
169
174
|
if self._tip != MB():
|
170
175
|
return self._tip.get().fold(f, init)
|
171
|
-
|
176
|
+
|
177
|
+
if init is not None:
|
172
178
|
return init
|
173
|
-
|
174
|
-
|
175
|
-
|
179
|
+
|
180
|
+
msg = 'SE: Folding empty SplitEnd but no initial value supplied'
|
181
|
+
raise ValueError(msg)
|
182
|
+
|
176
183
|
|
177
184
|
def SE[D](*ds: D) -> SplitEnd[D]:
|
178
185
|
return SplitEnd(ds)
|
179
|
-
|
dtools/datastructures/tuples.py
CHANGED
@@ -12,30 +12,38 @@
|
|
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
|
|
31
32
|
from __future__ import annotations
|
32
33
|
|
33
34
|
from collections.abc import Callable, Iterable, Iterator, Sequence
|
34
|
-
from typing import cast, overload
|
35
|
+
from typing import cast, overload, TypeVar
|
35
36
|
from dtools.fp.iterables import FM, accumulate, concat, exhaust, merge
|
36
37
|
|
37
38
|
__all__ = ['FTuple', 'FT']
|
38
39
|
|
40
|
+
D = TypeVar('D') # Not needed for mypy, hint for pdoc.
|
41
|
+
E = TypeVar('E')
|
42
|
+
L = TypeVar('L')
|
43
|
+
R = TypeVar('R')
|
44
|
+
U = TypeVar('U')
|
45
|
+
|
46
|
+
|
39
47
|
class FTuple[D](Sequence[D]):
|
40
48
|
"""
|
41
49
|
#### Functional Tuple
|
@@ -47,7 +55,8 @@ class FTuple[D](Sequence[D]):
|
|
47
55
|
* both left and right int multiplication supported
|
48
56
|
|
49
57
|
"""
|
50
|
-
|
58
|
+
|
59
|
+
__slots__ = ('_ds',)
|
51
60
|
|
52
61
|
def __init__(self, *dss: Iterable[D]) -> None:
|
53
62
|
if len(dss) < 2:
|
@@ -63,7 +72,7 @@ class FTuple[D](Sequence[D]):
|
|
63
72
|
return reversed(self._ds)
|
64
73
|
|
65
74
|
def __bool__(self) -> bool:
|
66
|
-
return bool(
|
75
|
+
return bool(self._ds)
|
67
76
|
|
68
77
|
def __len__(self) -> int:
|
69
78
|
return len(self._ds)
|
@@ -72,7 +81,7 @@ class FTuple[D](Sequence[D]):
|
|
72
81
|
return 'FT(' + ', '.join(map(repr, self)) + ')'
|
73
82
|
|
74
83
|
def __str__(self) -> str:
|
75
|
-
return
|
84
|
+
return '((' + ', '.join(map(repr, self)) + '))'
|
76
85
|
|
77
86
|
def __eq__(self, other: object, /) -> bool:
|
78
87
|
if self is other:
|
@@ -86,16 +95,18 @@ class FTuple[D](Sequence[D]):
|
|
86
95
|
@overload
|
87
96
|
def __getitem__(self, idx: slice, /) -> FTuple[D]: ...
|
88
97
|
|
89
|
-
def __getitem__(self, idx: slice|int, /) -> FTuple[D]|D:
|
98
|
+
def __getitem__(self, idx: slice | int, /) -> FTuple[D] | D:
|
90
99
|
if isinstance(idx, slice):
|
91
100
|
return FTuple(self._ds[idx])
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
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:
|
99
110
|
"""
|
100
111
|
**Fold Left**
|
101
112
|
|
@@ -118,10 +129,13 @@ class FTuple[D](Sequence[D]):
|
|
118
129
|
acc = f(acc, v)
|
119
130
|
return acc
|
120
131
|
|
121
|
-
def foldR[R](
|
122
|
-
|
123
|
-
|
124
|
-
|
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:
|
125
139
|
"""
|
126
140
|
**Fold Right**
|
127
141
|
|
@@ -153,7 +167,7 @@ class FTuple[D](Sequence[D]):
|
|
153
167
|
"""
|
154
168
|
return FTuple(self)
|
155
169
|
|
156
|
-
def __add__[E](self, other: FTuple[E], /) -> FTuple[D|E]:
|
170
|
+
def __add__[E](self, other: FTuple[E], /) -> FTuple[D | E]:
|
157
171
|
return FTuple(concat(self, other))
|
158
172
|
|
159
173
|
def __mul__(self, num: int, /) -> FTuple[D]:
|
@@ -162,7 +176,9 @@ class FTuple[D](Sequence[D]):
|
|
162
176
|
def __rmul__(self, num: int, /) -> FTuple[D]:
|
163
177
|
return FTuple(self._ds.__mul__(num if num > 0 else 0))
|
164
178
|
|
165
|
-
def accummulate[L](
|
179
|
+
def accummulate[L](
|
180
|
+
self, f: Callable[[L, D], L], s: L | None = None, /
|
181
|
+
) -> FTuple[L]:
|
166
182
|
"""
|
167
183
|
**Accumulate partial folds**
|
168
184
|
|
@@ -172,13 +188,14 @@ class FTuple[D](Sequence[D]):
|
|
172
188
|
"""
|
173
189
|
if s is None:
|
174
190
|
return FTuple(accumulate(self, f))
|
175
|
-
|
176
|
-
return FTuple(accumulate(self, f, s))
|
191
|
+
return FTuple(accumulate(self, f, s))
|
177
192
|
|
178
193
|
def map[U](self, f: Callable[[D], U], /) -> FTuple[U]:
|
179
194
|
return FTuple(map(f, self))
|
180
195
|
|
181
|
-
def bind[U](
|
196
|
+
def bind[U](
|
197
|
+
self, f: Callable[[D], FTuple[U]], type: FM = FM.CONCAT, /
|
198
|
+
) -> FTuple[U]:
|
182
199
|
"""
|
183
200
|
Bind function `f` to the `FTuple`.
|
184
201
|
|
@@ -189,15 +206,15 @@ class FTuple[D](Sequence[D]):
|
|
189
206
|
"""
|
190
207
|
match type:
|
191
208
|
case FM.CONCAT:
|
192
|
-
return FTuple(concat(*map(
|
209
|
+
return FTuple(concat(*map(f, self)))
|
193
210
|
case FM.MERGE:
|
194
|
-
return FTuple(merge(*map(
|
211
|
+
return FTuple(merge(*map(f, self)))
|
195
212
|
case FM.EXHAUST:
|
196
|
-
return FTuple(exhaust(*map(
|
213
|
+
return FTuple(exhaust(*map(f, self)))
|
197
214
|
case '*':
|
198
215
|
raise ValueError('Unknown FM type')
|
199
216
|
|
217
|
+
|
200
218
|
def FT[D](*ds: D) -> FTuple[D]:
|
201
219
|
"""Return an FTuple whose values are the function arguments."""
|
202
220
|
return FTuple(ds)
|
203
|
-
|
@@ -1,39 +1,48 @@
|
|
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
|
-
Requires-Dist: dtools.fp >= 1.
|
18
|
+
Requires-Dist: dtools.fp >= 1.4.0, < 1.5
|
18
19
|
Requires-Dist: pytest >=8.3.2 ; extra == "test"
|
19
20
|
Project-URL: Changelog, https://github.com/grscheller/dtools-datastructures/blob/main/CHANGELOG.md
|
20
21
|
Project-URL: Documentation, https://grscheller.github.io/dtools-docs/datastructures
|
21
22
|
Project-URL: Source, https://github.com/grscheller/dtools-datastructures
|
22
23
|
Provides-Extra: test
|
23
24
|
|
24
|
-
#
|
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
|
|
42
|
+
This project is part of the
|
43
|
+
[Developer Tools for Python][4] **dtools.** namespace project.
|
35
44
|
|
36
|
-
|
45
|
+
## Overview
|
37
46
|
|
38
47
|
Data structures allowing developers to focus on the algorithms they are
|
39
48
|
using instead of all the "bit fiddling" required to implement behaviors,
|
@@ -45,21 +54,22 @@ introducing inordinate complexity. Some of these data structures allow
|
|
45
54
|
data to be safely shared between multiple data structure instances by
|
46
55
|
making shared data immutable and inaccessible to client code.
|
47
56
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
55
64
|
|
56
65
|
Sometimes the real power of a data structure comes not from what it
|
57
66
|
empowers you to do, but from what it prevents you from doing to
|
58
67
|
yourself.
|
59
68
|
|
60
|
-
|
69
|
+
______________________________________________________________________
|
61
70
|
|
62
|
-
[1]: https://pypi.org/project/
|
71
|
+
[1]: https://pypi.org/project/dtools.datastructures/
|
63
72
|
[2]: https://github.com/grscheller/dtools-datastructures/
|
64
73
|
[3]: https://grscheller.github.io/dtools-docs/datastructures/
|
74
|
+
[4]: https://github.com/grscheller/dtools-docs
|
65
75
|
|
@@ -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=6GNS6Y94Af0IeRhKdQlUoPsKK1BLfpeSa_wjxXjz4uw,1163
|
2
|
-
dtools/datastructures/nodes.py,sha256=surkupETxZUAZeuhf8wM8BUWbP98trmqx5fczx32IU4,5695
|
3
|
-
dtools/datastructures/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
|
-
dtools/datastructures/queues.py,sha256=30rBRKZRCwYLLog2SIFLxqb5RzMvTfxroZK_K2FOfoM,12119
|
5
|
-
dtools/datastructures/tuples.py,sha256=88-e9Dpoeku8sYa3cmDFX17D80LiIb7lFHC2CGa6OnU,6583
|
6
|
-
dtools/datastructures/splitends/__init__.py,sha256=y0BpPKBg6g9GtcHwhEhQTwnAIdpxvhVD09vE1AMPYPM,940
|
7
|
-
dtools/datastructures/splitends/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
-
dtools/datastructures/splitends/se.py,sha256=Anm7fWa9A0hB6v6I3SVg-ZMm47J5LblAyyOV5gMnli8,5600
|
9
|
-
dtools_datastructures-0.25.0.dist-info/LICENSE,sha256=csqbZRvA3Nyuav1aszWvswE8CZtaKr-hMjjjcKqms7w,10774
|
10
|
-
dtools_datastructures-0.25.0.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
|
11
|
-
dtools_datastructures-0.25.0.dist-info/METADATA,sha256=reT_ouL-PHBhasetsZDmhTrjrwqp6aXAvhMe2PA18f4,2625
|
12
|
-
dtools_datastructures-0.25.0.dist-info/RECORD,,
|
{dtools_datastructures-0.25.0.dist-info → dtools_datastructures-0.25.2.dist-info/licenses}/LICENSE
RENAMED
File without changes
|