dtools.circular-array 3.12.1__py3-none-any.whl → 3.13.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dtools/circular_array/__init__.py +2 -3
- dtools/circular_array/ca.py +49 -46
- {dtools_circular_array-3.12.1.dist-info → dtools_circular_array-3.13.0.dist-info}/METADATA +15 -13
- dtools_circular_array-3.13.0.dist-info/RECORD +7 -0
- dtools_circular_array-3.12.1.dist-info/RECORD +0 -7
- {dtools_circular_array-3.12.1.dist-info → dtools_circular_array-3.13.0.dist-info}/WHEEL +0 -0
- {dtools_circular_array-3.12.1.dist-info → dtools_circular_array-3.13.0.dist-info}/licenses/LICENSE +0 -0
@@ -19,15 +19,14 @@ data structure with amortized O(1) pushes and pops either end.
|
|
19
19
|
|
20
20
|
Circular array data structure.
|
21
21
|
|
22
|
-
- *module* dtools.circular_array
|
22
|
+
- *module* dtools.circular_array.ca
|
23
23
|
- *class* dtools.circular_array.ca.CA
|
24
24
|
- initializer takes up to 1 iterable
|
25
25
|
- *function* dtools.circular_array.ca.ca
|
26
|
-
-
|
26
|
+
- constructs a `CA` from a variable number of arguments
|
27
27
|
|
28
28
|
"""
|
29
29
|
|
30
|
-
__version__ = '3.12.1'
|
31
30
|
__author__ = 'Geoffrey R. Scheller'
|
32
31
|
__copyright__ = 'Copyright (c) 2023-2025 Geoffrey R. Scheller'
|
33
32
|
__license__ = 'Apache License 2.0'
|
dtools/circular_array/ca.py
CHANGED
@@ -12,41 +12,42 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
"""###
|
15
|
+
"""### An indexable circular array data structure."""
|
16
16
|
|
17
17
|
from __future__ import annotations
|
18
|
+
|
18
19
|
from collections.abc import Callable, Iterable, Iterator
|
19
20
|
from typing import cast, Never, overload, TypeVar
|
20
21
|
|
21
22
|
__all__ = ['CA', 'ca']
|
22
23
|
|
23
|
-
D = TypeVar('D')
|
24
|
-
L = TypeVar('L') # ignored by both MyPy and Python. Makes linters unhappy
|
25
|
-
R = TypeVar('R') # when these are used on function and method signatures due
|
26
|
-
U = TypeVar('U') # to "redefined-outer-name" warnings. Function and method
|
27
|
-
T = TypeVar('T') # signatures do not support variance and bounds constraints.
|
24
|
+
D = TypeVar('D')
|
28
25
|
|
29
26
|
|
30
|
-
class CA[D]
|
27
|
+
class CA[D]:
|
31
28
|
"""Indexable circular array data structure
|
32
29
|
|
33
30
|
- generic, stateful data structure
|
34
|
-
- lowercase class name chosen to match nomenclature for builtins
|
35
|
-
- like `list` and `tuple`
|
36
31
|
- amortized O(1) pushing and popping from either end
|
37
32
|
- O(1) random access any element
|
38
33
|
- will resize itself as needed
|
39
34
|
- sliceable
|
40
35
|
- makes defensive copies of contents for the purposes of iteration
|
41
|
-
- in boolean context returns
|
42
|
-
|
36
|
+
- in boolean context returns
|
37
|
+
- `True` when not empty
|
38
|
+
- `False` when empty
|
39
|
+
- in comparisons compare identity before equality, like builtins do
|
43
40
|
- raises `IndexError` for out-of-bounds indexing
|
44
|
-
- raises `ValueError` for popping from or folding an empty `
|
41
|
+
- raises `ValueError` for popping from or folding an empty `CA`
|
45
42
|
|
46
43
|
"""
|
47
44
|
|
48
45
|
__slots__ = '_data', '_cnt', '_cap', '_front', '_rear'
|
49
46
|
|
47
|
+
L = TypeVar('L')
|
48
|
+
R = TypeVar('R')
|
49
|
+
U = TypeVar('U')
|
50
|
+
|
50
51
|
def __init__(self, *dss: Iterable[D]) -> None:
|
51
52
|
if len(dss) < 2:
|
52
53
|
self._data: list[D | None] = (
|
@@ -77,7 +78,6 @@ class CA[D]():
|
|
77
78
|
self._front, self._cap = self._front + self._cap, 2 * self._cap
|
78
79
|
|
79
80
|
def _compact_storage_capacity(self) -> None:
|
80
|
-
"""Compact the CA."""
|
81
81
|
match self._cnt:
|
82
82
|
case 0:
|
83
83
|
self._cap, self._front, self._rear, self._data = 2, 0, 1, [None, None]
|
@@ -211,11 +211,11 @@ class CA[D]():
|
|
211
211
|
raise IndexError(msg1 + msg2 + msg3)
|
212
212
|
|
213
213
|
@overload
|
214
|
-
def __delitem__(self, idx: int) -> None: ...
|
214
|
+
def __delitem__(self, idx: int, /) -> None: ...
|
215
215
|
@overload
|
216
|
-
def __delitem__(self, idx: slice) -> None: ...
|
216
|
+
def __delitem__(self, idx: slice, /) -> None: ...
|
217
217
|
|
218
|
-
def __delitem__(self, idx: int | slice) -> None:
|
218
|
+
def __delitem__(self, idx: int | slice, /) -> None:
|
219
219
|
data = list(self)
|
220
220
|
del data[idx]
|
221
221
|
_ca = CA(data)
|
@@ -259,7 +259,11 @@ class CA[D]():
|
|
259
259
|
return True
|
260
260
|
|
261
261
|
def pushl(self, *ds: D) -> None:
|
262
|
-
"""Push
|
262
|
+
"""Push left.
|
263
|
+
|
264
|
+
- push data from the left onto the CA
|
265
|
+
|
266
|
+
"""
|
263
267
|
for d in ds:
|
264
268
|
if self._cnt == self._cap:
|
265
269
|
self._double_storage_capacity()
|
@@ -267,7 +271,11 @@ class CA[D]():
|
|
267
271
|
self._data[self._front], self._cnt = d, self._cnt + 1
|
268
272
|
|
269
273
|
def pushr(self, *ds: D) -> None:
|
270
|
-
"""Push
|
274
|
+
"""Push right.
|
275
|
+
|
276
|
+
- push data from the right onto the CA
|
277
|
+
|
278
|
+
"""
|
271
279
|
for d in ds:
|
272
280
|
if self._cnt == self._cap:
|
273
281
|
self._double_storage_capacity()
|
@@ -275,9 +283,10 @@ class CA[D]():
|
|
275
283
|
self._data[self._rear], self._cnt = d, self._cnt + 1
|
276
284
|
|
277
285
|
def popl(self) -> D | Never:
|
278
|
-
"""Pop
|
286
|
+
"""Pop left.
|
279
287
|
|
280
|
-
|
288
|
+
- pop one value off the left side of the `CA`
|
289
|
+
- raises `ValueError` when called on an empty `CA`
|
281
290
|
|
282
291
|
"""
|
283
292
|
if self._cnt > 1:
|
@@ -301,9 +310,10 @@ class CA[D]():
|
|
301
310
|
return cast(D, d)
|
302
311
|
|
303
312
|
def popr(self) -> D | Never:
|
304
|
-
"""Pop
|
313
|
+
"""Pop right
|
305
314
|
|
306
|
-
|
315
|
+
- pop one value off the right side of the `CA`
|
316
|
+
- raises `ValueError` when called on an empty `CA`
|
307
317
|
|
308
318
|
"""
|
309
319
|
if self._cnt > 1:
|
@@ -330,7 +340,7 @@ class CA[D]():
|
|
330
340
|
"""Pop one value from left, provide a mandatory default value.
|
331
341
|
|
332
342
|
- safe version of popl
|
333
|
-
- returns
|
343
|
+
- returns the default value if `CA` is empty
|
334
344
|
|
335
345
|
"""
|
336
346
|
try:
|
@@ -342,7 +352,7 @@ class CA[D]():
|
|
342
352
|
"""Pop one value from right, provide a mandatory default value.
|
343
353
|
|
344
354
|
- safe version of popr
|
345
|
-
- returns
|
355
|
+
- returns the default value if `CA` is empty
|
346
356
|
|
347
357
|
"""
|
348
358
|
try:
|
@@ -353,9 +363,8 @@ class CA[D]():
|
|
353
363
|
def poplt(self, maximum: int, /) -> tuple[D, ...]:
|
354
364
|
"""Pop multiple values from left side of `CA`.
|
355
365
|
|
356
|
-
- returns the results in a tuple of type
|
357
|
-
-
|
358
|
-
- pop no more that `max` values
|
366
|
+
- returns the results in a tuple of type
|
367
|
+
- pop no more that `maximum` values
|
359
368
|
- will pop less if `CA` becomes empty
|
360
369
|
|
361
370
|
"""
|
@@ -374,9 +383,8 @@ class CA[D]():
|
|
374
383
|
def poprt(self, maximum: int, /) -> tuple[D, ...]:
|
375
384
|
"""Pop multiple values from right side of `CA`.
|
376
385
|
|
377
|
-
- returns the results in a tuple
|
378
|
-
-
|
379
|
-
- pop no more that `max` values
|
386
|
+
- returns the results in a tuple
|
387
|
+
- pop no more that `maximum` values
|
380
388
|
- will pop less if `CA` becomes empty
|
381
389
|
|
382
390
|
"""
|
@@ -392,34 +400,31 @@ class CA[D]():
|
|
392
400
|
return tuple(ds)
|
393
401
|
|
394
402
|
def rotl(self, n: int = 1, /) -> None:
|
395
|
-
"""Rotate `CA`
|
403
|
+
"""Rotate `CA` components to the left n times."""
|
396
404
|
if self._cnt < 2:
|
397
405
|
return
|
398
406
|
for _ in range(n, 0, -1):
|
399
407
|
self.pushr(self.popl())
|
400
408
|
|
401
409
|
def rotr(self, n: int = 1, /) -> None:
|
402
|
-
"""Rotate `CA`
|
410
|
+
"""Rotate `CA` components to the right n times."""
|
403
411
|
if self._cnt < 2:
|
404
412
|
return
|
405
413
|
for _ in range(n, 0, -1):
|
406
414
|
self.pushl(self.popr())
|
407
415
|
|
408
416
|
def map[U](self, f: Callable[[D], U], /) -> CA[U]:
|
409
|
-
"""Apply function f over
|
417
|
+
"""Apply function `f` over the `CA` contents,
|
410
418
|
|
411
|
-
-
|
412
|
-
- returns a new instance of type `CA[~U]`
|
419
|
+
- returns a new `CA` instance
|
413
420
|
|
414
421
|
"""
|
415
422
|
return CA(map(f, self))
|
416
423
|
|
417
|
-
def foldl[L](self, f: Callable[[L, D], L],
|
418
|
-
"""Left fold `CA`
|
424
|
+
def foldl[L](self, f: Callable[[L, D], L], initial: L | None = None, /) -> L:
|
425
|
+
"""Left fold `CA` with function `f` and an optional `initial` value.
|
419
426
|
|
420
|
-
-
|
421
|
-
- the first argument to `f` is for the accumulated value.
|
422
|
-
- parameter `initial` is an optional initial value
|
427
|
+
- first argument to `f` is for the accumulated value
|
423
428
|
- returns the reduced value of type `~L`
|
424
429
|
- note that `~L` and `~D` can be the same type
|
425
430
|
- if an initial value is not given then by necessity `~L = ~D`
|
@@ -443,12 +448,10 @@ class CA[D]():
|
|
443
448
|
acc = f(acc, d)
|
444
449
|
return acc
|
445
450
|
|
446
|
-
def foldr[R](self, f: Callable[[D, R], R],
|
447
|
-
"""Right fold `CA`
|
451
|
+
def foldr[R](self, f: Callable[[D, R], R], initial: R | None = None, /) -> R:
|
452
|
+
"""Right fold `CA` with function `f` and an optional `initial` value.
|
448
453
|
|
449
|
-
-
|
450
|
-
- the second argument to f is for the accumulated value
|
451
|
-
- parameter `initial` is an optional initial value
|
454
|
+
- second argument to f is for the accumulated value
|
452
455
|
- returns the reduced value of type `~R`
|
453
456
|
- note that `~R` and `~D` can be the same type
|
454
457
|
- if an initial value is not given then by necessity `~R = ~D`
|
@@ -497,6 +500,6 @@ class CA[D]():
|
|
497
500
|
self._front, self._rear = 0, self._cap - 1
|
498
501
|
|
499
502
|
|
500
|
-
def ca[
|
503
|
+
def ca[D](*ds: D) -> CA[D]:
|
501
504
|
"""Function to produce a `CA` array from a variable number of arguments."""
|
502
505
|
return CA(ds)
|
@@ -1,8 +1,8 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: dtools.circular-array
|
3
|
-
Version: 3.
|
3
|
+
Version: 3.13.0
|
4
4
|
Summary: ### Developer Tools - Circular Array Data Structure
|
5
|
-
Keywords: circular array,
|
5
|
+
Keywords: circular array,dequeue,pop,push,indexable,auto resizing
|
6
6
|
Author-email: "Geoffrey R. Scheller" <geoffrey@scheller.com>
|
7
7
|
Requires-Python: >=3.12
|
8
8
|
Description-Content-Type: text/markdown
|
@@ -17,6 +17,7 @@ License-File: LICENSE
|
|
17
17
|
Requires-Dist: pytest >=8.3.5 ; extra == "test"
|
18
18
|
Project-URL: Changelog, https://github.com/grscheller/dtools-circular-array/blob/main/CHANGELOG.md
|
19
19
|
Project-URL: Documentation, https://grscheller.github.io/dtools-docs/circular-array
|
20
|
+
Project-URL: Homepage, https://github.com/grscheller/dtools-docs
|
20
21
|
Project-URL: Source, https://github.com/grscheller/dtools-circular-array
|
21
22
|
Provides-Extra: test
|
22
23
|
|
@@ -39,24 +40,25 @@ namespace project.
|
|
39
40
|
- O(1) amortized pushes and pops either end.
|
40
41
|
- O(1) indexing
|
41
42
|
- fully supports slicing
|
42
|
-
- safely mutates
|
43
|
+
- safely mutates over previous state
|
43
44
|
|
44
45
|
### Module circular_array
|
45
46
|
|
46
|
-
A full featured
|
47
|
-
|
48
|
-
|
47
|
+
A full featured auto resizing circular array data structure. Double
|
48
|
+
sided, indexable, sliceable, and iterable. When iterated, uses cached
|
49
|
+
copies of its present state so that the circular array itself can safely
|
50
|
+
be mutated.
|
49
51
|
|
50
|
-
Useful either if used directly
|
51
|
-
|
52
|
+
Useful either if used directly like a Python list, or in a "has-a"
|
53
|
+
relationship when implementing other data structures.
|
52
54
|
|
53
55
|
- *module* dtools.circular_array
|
54
|
-
- *class*
|
55
|
-
- *function*
|
56
|
+
- *class* CA: circular array data structure
|
57
|
+
- *function* ca: factory function to produce a CA from data
|
56
58
|
|
57
|
-
Above nomenclature modeled after
|
58
|
-
`
|
59
|
-
|
59
|
+
Above nomenclature modeled after builtin data types like `list`, where
|
60
|
+
`CA` and `ca` correspond respectfully to `list` and `[]` in their use
|
61
|
+
cases.
|
60
62
|
|
61
63
|
#### Usage
|
62
64
|
|
@@ -0,0 +1,7 @@
|
|
1
|
+
dtools/circular_array/__init__.py,sha256=T_Jqa8AzDhow6q5O6A_FP5kCHBohEYJYSpo5xcsfTuU,1168
|
2
|
+
dtools/circular_array/ca.py,sha256=xSvYBPAFAJU_KbHAjmSIFAt3Pq9YkVVojo0BIoxIZPY,15918
|
3
|
+
dtools/circular_array/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
|
+
dtools_circular_array-3.13.0.dist-info/licenses/LICENSE,sha256=csqbZRvA3Nyuav1aszWvswE8CZtaKr-hMjjjcKqms7w,10774
|
5
|
+
dtools_circular_array-3.13.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
6
|
+
dtools_circular_array-3.13.0.dist-info/METADATA,sha256=NRnV41SCn3OKLWHgud6_WpQg_dr9WGbeXraJ6CkzAZg,3448
|
7
|
+
dtools_circular_array-3.13.0.dist-info/RECORD,,
|
@@ -1,7 +0,0 @@
|
|
1
|
-
dtools/circular_array/__init__.py,sha256=2CvFEORjHTaRYTteVH-Ifo7THhbttgif71VigBM-3-Y,1189
|
2
|
-
dtools/circular_array/ca.py,sha256=PRt-AOvR9WqwS6EdKFWXde7VxBc1yOD6kuCK_lXuWFQ,16683
|
3
|
-
dtools/circular_array/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
|
-
dtools_circular_array-3.12.1.dist-info/licenses/LICENSE,sha256=csqbZRvA3Nyuav1aszWvswE8CZtaKr-hMjjjcKqms7w,10774
|
5
|
-
dtools_circular_array-3.12.1.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
6
|
-
dtools_circular_array-3.12.1.dist-info/METADATA,sha256=9HGyzkYTReGc9LUbP_X1qbWEK-EQKVwA_gAaZdI_5cI,3495
|
7
|
-
dtools_circular_array-3.12.1.dist-info/RECORD,,
|
File without changes
|
{dtools_circular_array-3.12.1.dist-info → dtools_circular_array-3.13.0.dist-info}/licenses/LICENSE
RENAMED
File without changes
|