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.
@@ -13,19 +13,20 @@
13
13
  # limitations under the License.
14
14
 
15
15
  """
16
- ### package datastructures
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: nodes for graph-like data structures
23
- * package dtools.datastructures.splitends: data sharing between mutable objects
24
- * module dtools.datastructures.tuples: tuple-like data structures
25
- * module dtools.datastructures.queues: queue data structures
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.0"
29
- __author__ = "Geoffrey R. Scheller"
30
- __copyright__ = "Copyright (c) 2023-2024 Geoffrey R. Scheller"
31
- __license__ = "Apache License 2.0"
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'
@@ -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 Callable, cast, Iterator
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
- class SL_Node[D]():
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
- elif self._data == other._data:
78
+
79
+ if self._data == other._data:
71
80
  return True
72
- else:
73
- return False
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
- else:
82
- return self.data_eq(other)
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, f: Callable[[T, D], T], init: T|None=None) -> T:
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
- class DL_Node[D]():
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
- class Tree_Node[D, M]():
162
- """Binary Tree Node with metadata.
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__(self, data: D,
173
- up: MB[Tree_Node[D,M]],
174
- left: MB[Tree_Node[D,M]],
175
- right: MB[Tree_Node[D,M]],
176
- meta: tuple[M, ...] = ()):
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
- if self._up == MB():
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()
@@ -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
- """### Queue based data structures
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 **FIFOQueue:** First-In-First-Out Queue
25
- * function **FQ:** Constructs a FIFOQueue from a variable number of arguments
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 **LIFOQueue:** Last-In-First-Out Queue
32
- * function **LQ:** Constructs a LIFOQueue from a variable number of arguments
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 **DoubleQueue:** Double-Ended Queue
39
- * function **DQ:** Constructs a DoubleQueue from a variable number of arguments
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__ = [ 'DoubleQueue', 'FIFOQueue', 'LIFOQueue', 'QueueBase',
50
- 'DQ', 'FQ', 'LQ' ]
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
- __slots__ = '_ca'
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 "<< " + " < ".join(map(str, self)) + " <<"
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
- else:
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 "|| " + " > ".join(map(str, self)) + " ><"
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
- else:
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
- else:
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
- else:
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 ">< " + " | ".join(map(str, self)) + " ><"
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
- else:
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
- else:
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
- else:
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 2023-2025 Geoffrey R. Scheller
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
- __author__ = "Geoffrey R. Scheller"
26
- __copyright__ = "Copyright (c) 2024 Geoffrey R. Scheller"
27
- __license__ = "Apache License 2.0"
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
- """### SplitEnd stack related data structures
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 cast, Never
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 ('>< ' + ' -> '.join(map(str, self)) + ' ||')
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
- else:
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
- else:
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
- elif init is not None:
176
+
177
+ if init is not None:
172
178
  return init
173
- else:
174
- msg = 'SE: Folding empty SplitEnd but no initial value supplied'
175
- raise ValueError(msg)
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
-
@@ -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
- """### Tuple based data structures
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
- __slots__ = '_ds'
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(len(self._ds))
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 "((" + ", ".join(map(repr, self)) + "))"
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
- else:
93
- return self._ds[idx]
94
-
95
- def foldL[L](self,
96
- f: Callable[[L, D], L], /,
97
- start: L|None=None,
98
- default: L|None=None) -> L|None:
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](self,
122
- f: Callable[[D, R], R], /,
123
- start: R|None=None,
124
- default: R|None=None) -> R|None:
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](self, f: Callable[[L, D], L], s: L|None=None, /) -> FTuple[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
- else:
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](self, f: Callable[[D], FTuple[U]], type: FM=FM.CONCAT, /) -> FTuple[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(lambda x: iter(x), map(f, self))))
209
+ return FTuple(concat(*map(f, self)))
193
210
  case FM.MERGE:
194
- return FTuple(merge(*map(lambda x: iter(x), map(f, self))))
211
+ return FTuple(merge(*map(f, self)))
195
212
  case FM.EXHAUST:
196
- return FTuple(exhaust(*map(lambda x: iter(x), map(f, self))))
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.3
1
+ Metadata-Version: 2.4
2
2
  Name: dtools.datastructures
3
- Version: 0.25.0
4
- Summary: ### package datastructures
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 :: 4 - Beta
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.3.0, < 1.4
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
- # Python Datastructures Useful for Algorithms
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
- * **Repositories**
30
- * [dtools.datastructures][1] project on *PyPI*
31
- * [Source code][2] on *GitHub*
32
- * **Detailed documentation**
33
- * [Detailed API documentation][3] on *GH-Pages*
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
- ### Overview
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
- * functional & imperative programming styles supported
49
- * functional programming encouraged
50
- * project endeavors to remain Pythonic
51
- * methods which mutate objects don't return anything
52
- * like Python lists
53
- * in caparisons identity is considered before equality
54
- * like Python builtins
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/grscheller.dtools-datastructures/
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,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: flit 3.10.1
2
+ Generator: flit 3.12.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -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,,