pythonic-fp-circulararray 5.3.3__py3-none-any.whl → 6.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pythonic_fp/circulararray/auto.py +230 -204
- pythonic_fp/circulararray/fixed.py +171 -152
- {pythonic_fp_circulararray-5.3.3.dist-info → pythonic_fp_circulararray-6.0.0.dist-info}/METADATA +27 -12
- pythonic_fp_circulararray-6.0.0.dist-info/RECORD +8 -0
- pythonic_fp/circulararray/__init__.pyi +0 -0
- pythonic_fp/circulararray/auto.pyi +0 -43
- pythonic_fp/circulararray/fixed.pyi +0 -32
- pythonic_fp_circulararray-5.3.3.dist-info/RECORD +0 -11
- {pythonic_fp_circulararray-5.3.3.dist-info → pythonic_fp_circulararray-6.0.0.dist-info}/WHEEL +0 -0
- {pythonic_fp_circulararray-5.3.3.dist-info → pythonic_fp_circulararray-6.0.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -18,39 +18,44 @@ Variable Storage Capacity
|
|
|
18
18
|
|
|
19
19
|
**Circular array with variable storage capacity.**
|
|
20
20
|
|
|
21
|
-
- O(1) pops either end
|
|
22
|
-
- O(1) amortized pushes either end
|
|
21
|
+
- O(1) pops either end
|
|
22
|
+
- O(1) amortized pushes either end
|
|
23
23
|
- O(1) indexing, fully supports slicing
|
|
24
24
|
- auto-resizing more storage capacity when necessary, manually compatible
|
|
25
|
-
- iterable,
|
|
25
|
+
- iterable, safely mutates while iterators iterating over previous state
|
|
26
26
|
- comparisons compare identity before equality, like builtins
|
|
27
27
|
- in boolean context, falsy when empty, otherwise truthy
|
|
28
|
-
-
|
|
28
|
+
- function ``ca`` produces auto-resizing circular array from arguments
|
|
29
29
|
|
|
30
30
|
"""
|
|
31
|
+
|
|
31
32
|
from collections.abc import Callable, Iterable, Iterator
|
|
32
|
-
from typing import cast, overload
|
|
33
|
+
from typing import cast, Final, overload
|
|
34
|
+
from pythonic_fp.gadgets.sentinels.novalue import NoValue
|
|
33
35
|
|
|
34
36
|
__all__ = ['CA', 'ca']
|
|
35
37
|
|
|
38
|
+
nada: Final[NoValue] = NoValue()
|
|
36
39
|
|
|
37
|
-
class CA[I]():
|
|
38
40
|
|
|
41
|
+
class CA[I]:
|
|
39
42
|
__slots__ = '_items', '_cnt', '_cap', '_front', '_rear'
|
|
40
43
|
|
|
41
|
-
def __init__(
|
|
42
|
-
self,
|
|
43
|
-
items: Iterable[I] | None = None
|
|
44
|
-
) -> None:
|
|
44
|
+
def __init__(self, *items: Iterable[I]) -> None:
|
|
45
45
|
"""
|
|
46
|
-
:param items:
|
|
47
|
-
:raises TypeError: When ``items`` not
|
|
46
|
+
:param items: "Optionally" takes a single iterable to populate circular array.
|
|
47
|
+
:raises TypeError: When ``items[0]`` not iterable.
|
|
48
|
+
:raises ValueError: If more than 1 iterable is given.
|
|
48
49
|
"""
|
|
49
|
-
if items
|
|
50
|
-
|
|
50
|
+
if (size := len(items)) > 1:
|
|
51
|
+
msg = f'CA expects at most 1 argument, got {size}'
|
|
52
|
+
raise ValueError(msg)
|
|
53
|
+
if size:
|
|
54
|
+
values: list[I | NoValue] = list(cast(Iterable[I | NoValue], items[0]))
|
|
55
|
+
self._items = [nada] + values + [nada]
|
|
51
56
|
else:
|
|
52
|
-
self._items = [
|
|
53
|
-
self._cap = cap
|
|
57
|
+
self._items = [nada, nada]
|
|
58
|
+
self._cap = (cap := len(self._items))
|
|
54
59
|
self._cnt = cap - 2
|
|
55
60
|
if cap == 2:
|
|
56
61
|
self._front = 0
|
|
@@ -62,88 +67,93 @@ class CA[I]():
|
|
|
62
67
|
def _double_storage_capacity(self) -> None:
|
|
63
68
|
if self._front <= self._rear:
|
|
64
69
|
(
|
|
65
|
-
|
|
66
|
-
|
|
70
|
+
self._items,
|
|
71
|
+
self._cap,
|
|
67
72
|
) = (
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
73
|
+
self._items + [nada] * self._cap,
|
|
74
|
+
self._cap * 2,
|
|
75
|
+
)
|
|
71
76
|
else:
|
|
72
77
|
(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
self._items,
|
|
79
|
+
self._front,
|
|
80
|
+
self._cap,
|
|
76
81
|
) = (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
82
|
+
self._items[: self._front]
|
|
83
|
+
+ [nada] * self._cap
|
|
84
|
+
+ self._items[self._front :],
|
|
85
|
+
self._front + self._cap,
|
|
86
|
+
2 * self._cap,
|
|
87
|
+
)
|
|
81
88
|
|
|
82
89
|
def _compact_storage_capacity(self) -> None:
|
|
83
90
|
match self._cnt:
|
|
84
91
|
case 0:
|
|
85
92
|
(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
93
|
+
self._cap,
|
|
94
|
+
self._front,
|
|
95
|
+
self._rear,
|
|
96
|
+
self._items,
|
|
90
97
|
) = (
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
98
|
+
2,
|
|
99
|
+
0,
|
|
100
|
+
1,
|
|
101
|
+
[nada, nada],
|
|
102
|
+
)
|
|
96
103
|
case 1:
|
|
97
104
|
(
|
|
105
|
+
self._cap,
|
|
106
|
+
self._front,
|
|
107
|
+
self._rear,
|
|
108
|
+
self._items,
|
|
109
|
+
) = (
|
|
110
|
+
3,
|
|
111
|
+
1,
|
|
112
|
+
1,
|
|
113
|
+
[nada, self._items[self._front], nada],
|
|
114
|
+
)
|
|
115
|
+
case _:
|
|
116
|
+
if self._front <= self._rear:
|
|
117
|
+
(
|
|
98
118
|
self._cap,
|
|
99
119
|
self._front,
|
|
100
120
|
self._rear,
|
|
101
121
|
self._items,
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
1,
|
|
122
|
+
) = (
|
|
123
|
+
self._cnt + 2,
|
|
105
124
|
1,
|
|
106
|
-
|
|
125
|
+
self._cnt,
|
|
126
|
+
[nada] + self._items[self._front : self._rear + 1] + [nada],
|
|
107
127
|
)
|
|
108
|
-
case _:
|
|
109
|
-
if self._front <= self._rear:
|
|
110
|
-
(
|
|
111
|
-
self._cap,
|
|
112
|
-
self._front,
|
|
113
|
-
self._rear,
|
|
114
|
-
self._items,
|
|
115
|
-
) = (
|
|
116
|
-
self._cnt + 2,
|
|
117
|
-
1,
|
|
118
|
-
self._cnt,
|
|
119
|
-
[None] + self._items[self._front : self._rear + 1] + [None],
|
|
120
|
-
)
|
|
121
128
|
else:
|
|
122
129
|
(
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
130
|
+
self._cap,
|
|
131
|
+
self._front,
|
|
132
|
+
self._rear,
|
|
133
|
+
self._items,
|
|
127
134
|
) = (
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
135
|
+
self._cnt + 2,
|
|
136
|
+
1,
|
|
137
|
+
self._cnt,
|
|
138
|
+
[nada]
|
|
139
|
+
+ self._items[self._front :]
|
|
140
|
+
+ self._items[: self._rear + 1]
|
|
141
|
+
+ [nada],
|
|
142
|
+
)
|
|
133
143
|
|
|
134
144
|
def __iter__(self) -> Iterator[I]:
|
|
135
145
|
if self._cnt > 0:
|
|
136
146
|
(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
147
|
+
capacity,
|
|
148
|
+
rear,
|
|
149
|
+
position,
|
|
150
|
+
current_state,
|
|
141
151
|
) = (
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
152
|
+
self._cap,
|
|
153
|
+
self._rear,
|
|
154
|
+
self._front,
|
|
155
|
+
self._items.copy(),
|
|
156
|
+
)
|
|
147
157
|
|
|
148
158
|
while position != rear:
|
|
149
159
|
yield cast(I, current_state[position])
|
|
@@ -153,16 +163,16 @@ class CA[I]():
|
|
|
153
163
|
def __reversed__(self) -> Iterator[I]:
|
|
154
164
|
if self._cnt > 0:
|
|
155
165
|
(
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
166
|
+
capacity,
|
|
167
|
+
front,
|
|
168
|
+
position,
|
|
169
|
+
current_state,
|
|
160
170
|
) = (
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
171
|
+
self._cap,
|
|
172
|
+
self._front,
|
|
173
|
+
self._rear,
|
|
174
|
+
self._items.copy(),
|
|
175
|
+
)
|
|
166
176
|
|
|
167
177
|
while position != front:
|
|
168
178
|
yield cast(I, current_state[position])
|
|
@@ -184,9 +194,9 @@ class CA[I]():
|
|
|
184
194
|
@overload
|
|
185
195
|
def __getitem__(self, idx: int) -> I: ...
|
|
186
196
|
@overload
|
|
187
|
-
def __getitem__(self, idx: slice) ->
|
|
197
|
+
def __getitem__(self, idx: slice) -> 'CA[I]': ...
|
|
188
198
|
|
|
189
|
-
def __getitem__(self, idx: int | slice) -> I |
|
|
199
|
+
def __getitem__(self, idx: int | slice) -> I | 'CA[I]':
|
|
190
200
|
if isinstance(idx, slice):
|
|
191
201
|
return CA(list(self)[idx])
|
|
192
202
|
|
|
@@ -218,18 +228,18 @@ class CA[I]():
|
|
|
218
228
|
item_list[idx] = vals
|
|
219
229
|
_ca = CA(item_list)
|
|
220
230
|
(
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
231
|
+
self._items,
|
|
232
|
+
self._cnt,
|
|
233
|
+
self._cap,
|
|
234
|
+
self._front,
|
|
235
|
+
self._rear,
|
|
226
236
|
) = (
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
237
|
+
_ca._items,
|
|
238
|
+
_ca._cnt,
|
|
239
|
+
_ca._cap,
|
|
240
|
+
_ca._front,
|
|
241
|
+
_ca._rear,
|
|
242
|
+
)
|
|
233
243
|
return
|
|
234
244
|
|
|
235
245
|
msg = 'must assign iterable to extended slice'
|
|
@@ -259,18 +269,18 @@ class CA[I]():
|
|
|
259
269
|
del item_list[idx]
|
|
260
270
|
_ca = CA(item_list)
|
|
261
271
|
(
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
272
|
+
self._items,
|
|
273
|
+
self._cnt,
|
|
274
|
+
self._cap,
|
|
275
|
+
self._front,
|
|
276
|
+
self._rear,
|
|
267
277
|
) = (
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
278
|
+
_ca._items,
|
|
279
|
+
_ca._cnt,
|
|
280
|
+
_ca._cap,
|
|
281
|
+
_ca._front,
|
|
282
|
+
_ca._rear,
|
|
283
|
+
)
|
|
274
284
|
del _ca
|
|
275
285
|
|
|
276
286
|
def __eq__(self, other: object) -> bool:
|
|
@@ -280,28 +290,34 @@ class CA[I]():
|
|
|
280
290
|
return False
|
|
281
291
|
|
|
282
292
|
(
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
293
|
+
front1,
|
|
294
|
+
cnt1,
|
|
295
|
+
capacity1,
|
|
296
|
+
front2,
|
|
297
|
+
cnt2,
|
|
298
|
+
capacity2,
|
|
289
299
|
) = (
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
if
|
|
300
|
+
self._front,
|
|
301
|
+
self._cnt,
|
|
302
|
+
self._cap,
|
|
303
|
+
other._front,
|
|
304
|
+
other._cnt,
|
|
305
|
+
other._cap,
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
if cnt1 != cnt2:
|
|
299
309
|
return False
|
|
300
310
|
|
|
301
|
-
for nn in range(
|
|
302
|
-
if
|
|
311
|
+
for nn in range(cnt1):
|
|
312
|
+
if (
|
|
313
|
+
self._items[(front1 + nn) % capacity1]
|
|
314
|
+
is other._items[(front2 + nn) % capacity2]
|
|
315
|
+
):
|
|
303
316
|
continue
|
|
304
|
-
if
|
|
317
|
+
if (
|
|
318
|
+
self._items[(front1 + nn) % capacity1]
|
|
319
|
+
!= other._items[(front2 + nn) % capacity2]
|
|
320
|
+
):
|
|
305
321
|
return False
|
|
306
322
|
return True
|
|
307
323
|
|
|
@@ -314,14 +330,14 @@ class CA[I]():
|
|
|
314
330
|
if self._cnt == self._cap:
|
|
315
331
|
self._double_storage_capacity()
|
|
316
332
|
(
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
333
|
+
self._front,
|
|
334
|
+
self._items[self._front],
|
|
335
|
+
self._cnt,
|
|
320
336
|
) = (
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
337
|
+
(self._front - 1) % self._cap,
|
|
338
|
+
item,
|
|
339
|
+
self._cnt + 1,
|
|
340
|
+
)
|
|
325
341
|
|
|
326
342
|
def pushr(self, *items: I) -> None:
|
|
327
343
|
"""Push ``items`` on from right.
|
|
@@ -332,14 +348,14 @@ class CA[I]():
|
|
|
332
348
|
if self._cnt == self._cap:
|
|
333
349
|
self._double_storage_capacity()
|
|
334
350
|
(
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
351
|
+
self._rear,
|
|
352
|
+
self._items[self._rear],
|
|
353
|
+
self._cnt,
|
|
338
354
|
) = (
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
355
|
+
(self._rear + 1) % self._cap,
|
|
356
|
+
item,
|
|
357
|
+
self._cnt + 1,
|
|
358
|
+
)
|
|
343
359
|
|
|
344
360
|
def popl(self) -> I:
|
|
345
361
|
"""Pop single item off from left side.
|
|
@@ -349,30 +365,30 @@ class CA[I]():
|
|
|
349
365
|
"""
|
|
350
366
|
if self._cnt > 1:
|
|
351
367
|
(
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
368
|
+
d,
|
|
369
|
+
self._items[self._front],
|
|
370
|
+
self._front,
|
|
371
|
+
self._cnt,
|
|
356
372
|
) = (
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
373
|
+
self._items[self._front],
|
|
374
|
+
nada,
|
|
375
|
+
(self._front + 1) % self._cap,
|
|
376
|
+
self._cnt - 1,
|
|
377
|
+
)
|
|
362
378
|
elif self._cnt == 1:
|
|
363
379
|
(
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
380
|
+
d,
|
|
381
|
+
self._items[self._front],
|
|
382
|
+
self._cnt,
|
|
383
|
+
self._front,
|
|
384
|
+
self._rear,
|
|
369
385
|
) = (
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
386
|
+
self._items[self._front],
|
|
387
|
+
nada,
|
|
388
|
+
0,
|
|
389
|
+
0,
|
|
390
|
+
self._cap - 1,
|
|
391
|
+
)
|
|
376
392
|
else:
|
|
377
393
|
msg = 'Method popl called on an empty CA'
|
|
378
394
|
raise ValueError(msg)
|
|
@@ -386,30 +402,30 @@ class CA[I]():
|
|
|
386
402
|
"""
|
|
387
403
|
if self._cnt > 1:
|
|
388
404
|
(
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
405
|
+
d,
|
|
406
|
+
self._items[self._rear],
|
|
407
|
+
self._rear,
|
|
408
|
+
self._cnt,
|
|
393
409
|
) = (
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
410
|
+
self._items[self._rear],
|
|
411
|
+
nada,
|
|
412
|
+
(self._rear - 1) % self._cap,
|
|
413
|
+
self._cnt - 1,
|
|
414
|
+
)
|
|
399
415
|
elif self._cnt == 1:
|
|
400
416
|
(
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
417
|
+
d,
|
|
418
|
+
self._items[self._front],
|
|
419
|
+
self._cnt,
|
|
420
|
+
self._front,
|
|
421
|
+
self._rear,
|
|
406
422
|
) = (
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
423
|
+
self._items[self._front],
|
|
424
|
+
nada,
|
|
425
|
+
0,
|
|
426
|
+
0,
|
|
427
|
+
self._cap - 1,
|
|
428
|
+
)
|
|
413
429
|
else:
|
|
414
430
|
msg = 'Method popr called on an empty CA'
|
|
415
431
|
raise ValueError(msg)
|
|
@@ -493,7 +509,7 @@ class CA[I]():
|
|
|
493
509
|
for _ in range(n, 0, -1):
|
|
494
510
|
self.pushl(self.popr())
|
|
495
511
|
|
|
496
|
-
def map[U](self, f: Callable[[I], U]) ->
|
|
512
|
+
def map[U](self, f: Callable[[I], U]) -> 'CA[U]':
|
|
497
513
|
"""Apply function ``f`` over the circular array's contents.
|
|
498
514
|
|
|
499
515
|
:param f: Callable from type ``I`` to type ``U``.
|
|
@@ -501,52 +517,62 @@ class CA[I]():
|
|
|
501
517
|
"""
|
|
502
518
|
return CA(map(f, self))
|
|
503
519
|
|
|
504
|
-
|
|
520
|
+
@overload
|
|
521
|
+
def foldl[L](self, f: Callable[[I, I], I]) -> I: ...
|
|
522
|
+
@overload
|
|
523
|
+
def foldl[L](self, f: Callable[[L, I], L], start: L) -> L: ...
|
|
524
|
+
|
|
525
|
+
def foldl[L](self, f: Callable[[L, I], L], start: L | NoValue = NoValue()) -> L:
|
|
505
526
|
"""Fold left with a function and optional starting item.
|
|
506
527
|
|
|
507
528
|
:param f: Folding function, first argument to ``f`` is for the accumulator.
|
|
508
|
-
:param start: Optional starting item.
|
|
529
|
+
:param start: "Optional" starting item.
|
|
509
530
|
:returns: Reduced value produced by the left fold.
|
|
510
531
|
:raises ValueError: When circular array empty and no starting item given.
|
|
511
532
|
"""
|
|
512
533
|
if self._cnt == 0:
|
|
513
|
-
if start is
|
|
534
|
+
if start is nada:
|
|
514
535
|
msg = 'Method foldl called on an empty CA without a start item.'
|
|
515
536
|
raise ValueError(msg)
|
|
516
|
-
return start
|
|
537
|
+
return cast(L, start)
|
|
517
538
|
|
|
518
|
-
if start is
|
|
539
|
+
if start is nada:
|
|
519
540
|
acc = cast(L, self[0]) # in this case D = L
|
|
520
541
|
for idx in range(1, self._cnt):
|
|
521
542
|
acc = f(acc, self[idx])
|
|
522
543
|
return acc
|
|
523
544
|
|
|
524
|
-
acc = start
|
|
545
|
+
acc = cast(L, start)
|
|
525
546
|
for d in self:
|
|
526
547
|
acc = f(acc, d)
|
|
527
548
|
return acc
|
|
528
549
|
|
|
529
|
-
|
|
550
|
+
@overload
|
|
551
|
+
def foldr[R](self, f: Callable[[I, I], I]) -> I: ...
|
|
552
|
+
@overload
|
|
553
|
+
def foldr[R](self, f: Callable[[I, R], R], start: R) -> R: ...
|
|
554
|
+
|
|
555
|
+
def foldr[R](self, f: Callable[[I, R], R], start: R | NoValue = nada) -> R:
|
|
530
556
|
"""Fold right with a function and an optional starting item.
|
|
531
557
|
|
|
532
558
|
:param f: Folding function, second argument to ``f`` is for the accumulator.
|
|
533
|
-
:param start: Optional starting item.
|
|
559
|
+
:param start: "Optional" starting item.
|
|
534
560
|
:returns: Reduced value produced by the right fold.
|
|
535
561
|
:raises ValueError: When circular array empty and no starting item given.
|
|
536
562
|
"""
|
|
537
563
|
if self._cnt == 0:
|
|
538
|
-
if start is
|
|
564
|
+
if start is nada:
|
|
539
565
|
msg = 'Method foldr called on empty CA without initial value.'
|
|
540
566
|
raise ValueError(msg)
|
|
541
|
-
return start
|
|
567
|
+
return cast(R, start)
|
|
542
568
|
|
|
543
|
-
if start is
|
|
569
|
+
if start is nada:
|
|
544
570
|
acc = cast(R, self[-1]) # in this case D = R
|
|
545
571
|
for idx in range(self._cnt - 2, -1, -1):
|
|
546
572
|
acc = f(self[idx], acc)
|
|
547
573
|
return acc
|
|
548
574
|
|
|
549
|
-
acc = start
|
|
575
|
+
acc = cast(R, start)
|
|
550
576
|
for d in reversed(self):
|
|
551
577
|
acc = f(d, acc)
|
|
552
578
|
return acc
|
|
@@ -561,16 +587,16 @@ class CA[I]():
|
|
|
561
587
|
def empty(self) -> None:
|
|
562
588
|
"""Empty the circular array, keep current storage capacity."""
|
|
563
589
|
(
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
590
|
+
self._items,
|
|
591
|
+
self._front,
|
|
592
|
+
self._rear,
|
|
593
|
+
self._cnt,
|
|
568
594
|
) = (
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
595
|
+
[nada] * self._cap,
|
|
596
|
+
0,
|
|
597
|
+
self._cap - 1,
|
|
598
|
+
0,
|
|
599
|
+
)
|
|
574
600
|
|
|
575
601
|
def fraction_filled(self) -> float:
|
|
576
602
|
"""Find fraction of the storage capacity which is filled.
|
|
@@ -589,12 +615,12 @@ class CA[I]():
|
|
|
589
615
|
self._compact_storage_capacity()
|
|
590
616
|
if (min_cap := minimum_capacity) > self._cap:
|
|
591
617
|
(
|
|
592
|
-
|
|
593
|
-
|
|
618
|
+
self._cap,
|
|
619
|
+
self._items,
|
|
594
620
|
) = (
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
621
|
+
min_cap,
|
|
622
|
+
self._items + [nada] * (min_cap - self._cap),
|
|
623
|
+
)
|
|
598
624
|
if self._cnt == 0:
|
|
599
625
|
self._front, self._rear = 0, self._cap - 1
|
|
600
626
|
|
|
@@ -18,89 +18,92 @@ Fixed Storage Capacity
|
|
|
18
18
|
|
|
19
19
|
**Circular array with fixed storage capacity.**
|
|
20
20
|
|
|
21
|
-
- O(1) pops and pushes either end
|
|
21
|
+
- O(1) pops and pushes either end
|
|
22
22
|
- O(1) indexing, does not support slicing
|
|
23
23
|
- fixed total storage capacity
|
|
24
|
-
- iterable,
|
|
24
|
+
- iterable, safely mutates while iterators iterating over previous state
|
|
25
25
|
- comparisons compare identity before equality, like builtins
|
|
26
26
|
- in boolean context, falsy when either empty or full, otherwise truthy
|
|
27
|
-
-
|
|
27
|
+
- function ``caf`` produces fixed capacity circular array from arguments
|
|
28
28
|
|
|
29
29
|
"""
|
|
30
|
+
|
|
30
31
|
from collections.abc import Callable, Iterable, Iterator
|
|
31
|
-
from typing import cast, Final
|
|
32
|
+
from typing import cast, Final, overload
|
|
33
|
+
from pythonic_fp.gadgets.sentinels.novalue import NoValue
|
|
32
34
|
|
|
33
35
|
__all__ = ['CAF', 'caf']
|
|
34
36
|
|
|
37
|
+
nada: Final[NoValue] = NoValue()
|
|
35
38
|
|
|
36
|
-
class CAF[I]():
|
|
37
39
|
|
|
40
|
+
class CAF[I]:
|
|
38
41
|
__slots__ = '_items', '_cnt', '_cap', '_front', '_rear'
|
|
39
42
|
|
|
40
|
-
def __init__(
|
|
41
|
-
self,
|
|
42
|
-
items: Iterable[I] | None = None,
|
|
43
|
-
capacity: int = 2
|
|
44
|
-
) -> None:
|
|
43
|
+
def __init__(self, *items: Iterable[I], cap: int = 2) -> None:
|
|
45
44
|
"""
|
|
46
|
-
:param items:
|
|
47
|
-
:param
|
|
48
|
-
:raises TypeError: When ``items`` not
|
|
45
|
+
:param items: "Optionally" takes a single iterable to populate circular array.
|
|
46
|
+
:param cap: Minimum fixed storage capacity of circular array.
|
|
47
|
+
:raises TypeError: When ``items[0]`` not iterable,
|
|
48
|
+
:raises ValueError: If more than 1 iterable is given.
|
|
49
49
|
"""
|
|
50
|
-
|
|
51
|
-
if items
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
cap = max(2, cap)
|
|
51
|
+
if (size := len(items)) > 1:
|
|
52
|
+
msg = f'CAF expects at most 1 argument, got {size}'
|
|
53
|
+
raise ValueError(msg)
|
|
54
|
+
if size:
|
|
55
|
+
values: list[I | NoValue] = list(cast(Iterable[I | NoValue], items[0]))
|
|
56
|
+
cnt = len(values)
|
|
57
|
+
cap = max(cnt, cap)
|
|
58
|
+
self._items = values + [nada] * (cap - cnt)
|
|
54
59
|
else:
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
self._cnt = count
|
|
61
|
-
if count == 0:
|
|
60
|
+
self._items = [nada] * cap
|
|
61
|
+
cnt = 0
|
|
62
|
+
self._cap: Final[int] = cap
|
|
63
|
+
self._cnt = cnt
|
|
64
|
+
if cnt == 0:
|
|
62
65
|
self._front = 0
|
|
63
|
-
self._rear =
|
|
66
|
+
self._rear = cap - 1
|
|
64
67
|
else:
|
|
65
68
|
self._front = 0
|
|
66
|
-
self._rear =
|
|
69
|
+
self._rear = cnt - 1
|
|
67
70
|
|
|
68
71
|
def __iter__(self) -> Iterator[I]:
|
|
69
72
|
if self._cnt > 0:
|
|
70
73
|
(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
cap,
|
|
75
|
+
rear,
|
|
76
|
+
position,
|
|
77
|
+
current_state,
|
|
75
78
|
) = (
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
self._cap,
|
|
80
|
+
self._rear,
|
|
81
|
+
self._front,
|
|
82
|
+
self._items.copy(),
|
|
83
|
+
)
|
|
81
84
|
|
|
82
85
|
while position != rear:
|
|
83
86
|
yield cast(I, current_state[position])
|
|
84
|
-
position = (position + 1) %
|
|
87
|
+
position = (position + 1) % cap
|
|
85
88
|
yield cast(I, current_state[position])
|
|
86
89
|
|
|
87
90
|
def __reversed__(self) -> Iterator[I]:
|
|
88
91
|
if self._cnt > 0:
|
|
89
92
|
(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
93
|
+
cap,
|
|
94
|
+
front,
|
|
95
|
+
position,
|
|
96
|
+
current_state,
|
|
94
97
|
) = (
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
self._cap,
|
|
99
|
+
self._front,
|
|
100
|
+
self._rear,
|
|
101
|
+
self._items.copy(),
|
|
102
|
+
)
|
|
100
103
|
|
|
101
104
|
while position != front:
|
|
102
105
|
yield cast(I, current_state[position])
|
|
103
|
-
position = (position - 1) %
|
|
106
|
+
position = (position - 1) % cap
|
|
104
107
|
yield cast(I, current_state[position])
|
|
105
108
|
|
|
106
109
|
def __repr__(self) -> str:
|
|
@@ -150,18 +153,18 @@ class CAF[I]():
|
|
|
150
153
|
def __delitem__(self, idx: int) -> None:
|
|
151
154
|
item_list = list(self)
|
|
152
155
|
del item_list[idx]
|
|
153
|
-
_ca = CAF(item_list, self._cap)
|
|
156
|
+
_ca = CAF(item_list, cap = self._cap)
|
|
154
157
|
(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
158
|
+
self._items,
|
|
159
|
+
self._cnt,
|
|
160
|
+
self._front,
|
|
161
|
+
self._rear,
|
|
159
162
|
) = (
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
_ca._items,
|
|
164
|
+
_ca._cnt,
|
|
165
|
+
_ca._front,
|
|
166
|
+
_ca._rear,
|
|
167
|
+
)
|
|
165
168
|
del _ca
|
|
166
169
|
|
|
167
170
|
def __eq__(self, other: object) -> bool:
|
|
@@ -171,28 +174,34 @@ class CAF[I]():
|
|
|
171
174
|
return False
|
|
172
175
|
|
|
173
176
|
(
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
177
|
+
front1,
|
|
178
|
+
cnt1,
|
|
179
|
+
cap1,
|
|
180
|
+
front2,
|
|
181
|
+
cnt2,
|
|
182
|
+
cap2,
|
|
180
183
|
) = (
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
if
|
|
184
|
+
self._front,
|
|
185
|
+
self._cnt,
|
|
186
|
+
self._cap,
|
|
187
|
+
other._front,
|
|
188
|
+
other._cnt,
|
|
189
|
+
other._cap,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
if cnt1 != cnt2:
|
|
190
193
|
return False
|
|
191
194
|
|
|
192
|
-
for nn in range(
|
|
193
|
-
if
|
|
195
|
+
for nn in range(cnt1):
|
|
196
|
+
if (
|
|
197
|
+
self._items[(front1 + nn) % cap1]
|
|
198
|
+
is other._items[(front2 + nn) % cap2]
|
|
199
|
+
):
|
|
194
200
|
continue
|
|
195
|
-
if
|
|
201
|
+
if (
|
|
202
|
+
self._items[(front1 + nn) % cap1]
|
|
203
|
+
!= other._items[(front2 + nn) % cap2]
|
|
204
|
+
):
|
|
196
205
|
return False
|
|
197
206
|
return True
|
|
198
207
|
|
|
@@ -207,14 +216,14 @@ class CAF[I]():
|
|
|
207
216
|
raise ValueError(msg)
|
|
208
217
|
|
|
209
218
|
(
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
219
|
+
self._front,
|
|
220
|
+
self._items[self._front],
|
|
221
|
+
self._cnt,
|
|
213
222
|
) = (
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
223
|
+
(self._front - 1) % self._cap,
|
|
224
|
+
item,
|
|
225
|
+
self._cnt + 1,
|
|
226
|
+
)
|
|
218
227
|
|
|
219
228
|
def pushr(self, item: I) -> None:
|
|
220
229
|
"""Push ``item`` on from Right.
|
|
@@ -227,14 +236,14 @@ class CAF[I]():
|
|
|
227
236
|
raise ValueError(msg)
|
|
228
237
|
|
|
229
238
|
(
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
239
|
+
self._rear,
|
|
240
|
+
self._items[self._rear],
|
|
241
|
+
self._cnt,
|
|
233
242
|
) = (
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
243
|
+
(self._rear + 1) % self._cap,
|
|
244
|
+
item,
|
|
245
|
+
self._cnt + 1,
|
|
246
|
+
)
|
|
238
247
|
|
|
239
248
|
def popl(self) -> I:
|
|
240
249
|
"""Pop single item off from left side.
|
|
@@ -244,30 +253,30 @@ class CAF[I]():
|
|
|
244
253
|
"""
|
|
245
254
|
if self._cnt > 1:
|
|
246
255
|
(
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
256
|
+
d,
|
|
257
|
+
self._items[self._front],
|
|
258
|
+
self._front,
|
|
259
|
+
self._cnt,
|
|
251
260
|
) = (
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
261
|
+
self._items[self._front],
|
|
262
|
+
nada,
|
|
263
|
+
(self._front + 1) % self._cap,
|
|
264
|
+
self._cnt - 1,
|
|
265
|
+
)
|
|
257
266
|
elif self._cnt == 1:
|
|
258
267
|
(
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
268
|
+
d,
|
|
269
|
+
self._items[self._front],
|
|
270
|
+
self._cnt,
|
|
271
|
+
self._front,
|
|
272
|
+
self._rear,
|
|
264
273
|
) = (
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
274
|
+
self._items[self._front],
|
|
275
|
+
nada,
|
|
276
|
+
0,
|
|
277
|
+
0,
|
|
278
|
+
self._cap - 1,
|
|
279
|
+
)
|
|
271
280
|
else:
|
|
272
281
|
msg = 'Method popl called on an empty CAF'
|
|
273
282
|
raise ValueError(msg)
|
|
@@ -281,30 +290,30 @@ class CAF[I]():
|
|
|
281
290
|
"""
|
|
282
291
|
if self._cnt > 1:
|
|
283
292
|
(
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
293
|
+
d,
|
|
294
|
+
self._items[self._rear],
|
|
295
|
+
self._rear,
|
|
296
|
+
self._cnt,
|
|
288
297
|
) = (
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
298
|
+
self._items[self._rear],
|
|
299
|
+
nada,
|
|
300
|
+
(self._rear - 1) % self._cap,
|
|
301
|
+
self._cnt - 1,
|
|
302
|
+
)
|
|
294
303
|
elif self._cnt == 1:
|
|
295
304
|
(
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
305
|
+
d,
|
|
306
|
+
self._items[self._front],
|
|
307
|
+
self._cnt,
|
|
308
|
+
self._front,
|
|
309
|
+
self._rear,
|
|
301
310
|
) = (
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
311
|
+
self._items[self._front],
|
|
312
|
+
nada,
|
|
313
|
+
0,
|
|
314
|
+
0,
|
|
315
|
+
self._cap - 1,
|
|
316
|
+
)
|
|
308
317
|
else:
|
|
309
318
|
msg = 'Method popr called on an empty CAF'
|
|
310
319
|
raise ValueError(msg)
|
|
@@ -388,15 +397,20 @@ class CAF[I]():
|
|
|
388
397
|
for _ in range(n, 0, -1):
|
|
389
398
|
self.pushl(self.popr())
|
|
390
399
|
|
|
391
|
-
def map[U](self, f: Callable[[I], U]) ->
|
|
400
|
+
def map[U](self, f: Callable[[I], U]) -> 'CAF[U]':
|
|
392
401
|
"""Apply function ``f`` over the circular array's contents,
|
|
393
402
|
|
|
394
403
|
:param f: Callable from type ``I`` to type ``U``.
|
|
395
404
|
:returns: New fixed capacity circular array instance.
|
|
396
405
|
"""
|
|
397
|
-
return CAF(map(f, self), self._cap)
|
|
406
|
+
return CAF(map(f, self), cap = self._cap)
|
|
407
|
+
|
|
408
|
+
@overload
|
|
409
|
+
def foldl[L](self, f: Callable[[I, I], I]) -> I: ...
|
|
410
|
+
@overload
|
|
411
|
+
def foldl[L](self, f: Callable[[L, I], L], start: L) -> L: ...
|
|
398
412
|
|
|
399
|
-
def foldl[L](self, f: Callable[[L, I], L], start: L |
|
|
413
|
+
def foldl[L](self, f: Callable[[L, I], L], start: L | NoValue = nada) -> L:
|
|
400
414
|
"""Fold left with a function and optional stating item.
|
|
401
415
|
|
|
402
416
|
:param f: Folding function, first argument to ``f`` is for the accumulator.
|
|
@@ -405,23 +419,28 @@ class CAF[I]():
|
|
|
405
419
|
:raises ValueError: When circular array empty and no starting item given.
|
|
406
420
|
"""
|
|
407
421
|
if self._cnt == 0:
|
|
408
|
-
if start is
|
|
422
|
+
if start is nada:
|
|
409
423
|
msg = 'Method foldl called on an empty CAF without a start item.'
|
|
410
424
|
raise ValueError(msg)
|
|
411
|
-
return start
|
|
425
|
+
return cast(L, start)
|
|
412
426
|
|
|
413
|
-
if start is
|
|
427
|
+
if start is nada:
|
|
414
428
|
acc = cast(L, self[0]) # in this case D = L
|
|
415
429
|
for idx in range(1, self._cnt):
|
|
416
430
|
acc = f(acc, self[idx])
|
|
417
431
|
return acc
|
|
418
432
|
|
|
419
|
-
acc = start
|
|
433
|
+
acc = cast(L, start)
|
|
420
434
|
for d in self:
|
|
421
435
|
acc = f(acc, d)
|
|
422
436
|
return acc
|
|
423
437
|
|
|
424
|
-
|
|
438
|
+
@overload
|
|
439
|
+
def foldr[R](self, f: Callable[[I, I], I]) -> I: ...
|
|
440
|
+
@overload
|
|
441
|
+
def foldr[R](self, f: Callable[[I, R], R], start: R) -> R: ...
|
|
442
|
+
|
|
443
|
+
def foldr[R](self, f: Callable[[I, R], R], start: R | NoValue = nada) -> R:
|
|
425
444
|
"""Fold right with a function and an optional starting item.
|
|
426
445
|
|
|
427
446
|
:param f: Folding function, second argument to ``f`` is for the accumulator.
|
|
@@ -430,18 +449,18 @@ class CAF[I]():
|
|
|
430
449
|
:raises ValueError: When circular array empty and no starting item given.
|
|
431
450
|
"""
|
|
432
451
|
if self._cnt == 0:
|
|
433
|
-
if start is
|
|
452
|
+
if start is nada:
|
|
434
453
|
msg = 'Method foldr called on empty CAF without initial value.'
|
|
435
454
|
raise ValueError(msg)
|
|
436
|
-
return start
|
|
455
|
+
return cast(R, start)
|
|
437
456
|
|
|
438
|
-
if start is
|
|
457
|
+
if start is nada:
|
|
439
458
|
acc = cast(R, self[-1]) # in this case D = R
|
|
440
459
|
for idx in range(self._cnt - 2, -1, -1):
|
|
441
460
|
acc = f(self[idx], acc)
|
|
442
461
|
return acc
|
|
443
462
|
|
|
444
|
-
acc = start
|
|
463
|
+
acc = cast(R, start)
|
|
445
464
|
for d in reversed(self):
|
|
446
465
|
acc = f(d, acc)
|
|
447
466
|
return acc
|
|
@@ -456,16 +475,16 @@ class CAF[I]():
|
|
|
456
475
|
def empty(self) -> None:
|
|
457
476
|
"""Empty the circular array."""
|
|
458
477
|
(
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
478
|
+
self._items,
|
|
479
|
+
self._front,
|
|
480
|
+
self._rear,
|
|
481
|
+
self._cnt,
|
|
463
482
|
) = (
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
483
|
+
[nada] * self._cap,
|
|
484
|
+
0,
|
|
485
|
+
self._cap - 1,
|
|
486
|
+
0,
|
|
487
|
+
)
|
|
469
488
|
|
|
470
489
|
def fraction_filled(self) -> float:
|
|
471
490
|
"""Find fraction of the storage capacity which is filled.
|
|
@@ -475,11 +494,11 @@ class CAF[I]():
|
|
|
475
494
|
return self._cnt / self._cap
|
|
476
495
|
|
|
477
496
|
|
|
478
|
-
def caf[T](*items: T,
|
|
497
|
+
def caf[T](*items: T, cap: int = 2) -> CAF[T]:
|
|
479
498
|
"""Produce a circular array from a variable number of arguments.
|
|
480
499
|
|
|
481
500
|
:param items: Initial items for a new fixed capacity :circular array.
|
|
482
|
-
:param
|
|
501
|
+
:param cap: The minimum storage capacity to set.
|
|
483
502
|
:returns: New fixed storage capacity circular array.
|
|
484
503
|
"""
|
|
485
|
-
return CAF(items,
|
|
504
|
+
return CAF(items, cap=cap)
|
{pythonic_fp_circulararray-5.3.3.dist-info → pythonic_fp_circulararray-6.0.0.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pythonic-fp-circulararray
|
|
3
|
-
Version:
|
|
3
|
+
Version: 6.0.0
|
|
4
4
|
Summary: Circular Array
|
|
5
5
|
Keywords: auto resizing,circular array,circulararray,dequeue,indexable,pop,push
|
|
6
6
|
Author-email: "Geoffrey R. Scheller" <geoffrey@scheller.com>
|
|
@@ -14,6 +14,7 @@ Classifier: Operating System :: OS Independent
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.13
|
|
15
15
|
Classifier: Typing :: Typed
|
|
16
16
|
License-File: LICENSE
|
|
17
|
+
Requires-Dist: pythonic-fp-gadgets>=3.1.0
|
|
17
18
|
Requires-Dist: pytest>=8.4.1 ; extra == "test"
|
|
18
19
|
Project-URL: Changelog, https://github.com/grscheller/pythonic-fp-circulararray/blob/main/CHANGELOG.rst
|
|
19
20
|
Project-URL: Documentation, https://grscheller.github.io/pythonic-fp/circulararray/development/build/html/index.html
|
|
@@ -30,17 +31,31 @@ PyPI project
|
|
|
30
31
|
|
|
31
32
|
Python module implementing stateful circular array data structures.
|
|
32
33
|
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
- O(1)
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
- variable storage capacity circular array
|
|
35
|
+
|
|
36
|
+
- O(1) pops either end
|
|
37
|
+
- O(1) amortized pushes either end
|
|
38
|
+
- O(1) indexing, fully supports slicing
|
|
39
|
+
- auto-resizing more storage capacity when necessary, manually compatible
|
|
40
|
+
- iterable, safely mutates while iterators iterating over previous state
|
|
41
|
+
- comparisons compare identity before equality, like builtins
|
|
42
|
+
- in boolean context, falsy when empty, otherwise truthy
|
|
43
|
+
- function ``ca`` produces auto-resizing circular array from arguments
|
|
44
|
+
|
|
45
|
+
- fixed storage capacity circular array
|
|
46
|
+
|
|
47
|
+
- O(1) pops and pushes either end
|
|
48
|
+
- O(1) indexing, does not support slicing
|
|
49
|
+
- fixed total storage capacity
|
|
50
|
+
- iterable, safely mutates while iterators iterating over previous state
|
|
51
|
+
- comparisons compare identity before equality, like builtins
|
|
52
|
+
- in boolean context, falsy when either empty or full, otherwise truthy
|
|
53
|
+
- function ``caf`` produces fixed capacity circular array from arguments
|
|
54
|
+
|
|
55
|
+
Part of the
|
|
56
|
+
`pythonic-fp
|
|
57
|
+
<https://grscheller.github.io/pythonic-fp/homepage/build/html/index.html>`_
|
|
58
|
+
PyPI projects.
|
|
44
59
|
|
|
45
60
|
Documentation
|
|
46
61
|
-------------
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
pythonic_fp/circulararray/__init__.py,sha256=uR0KASmw1Zraz9l3VPbqPm9WUKzWk-0_tUGT0opabzQ,1515
|
|
2
|
+
pythonic_fp/circulararray/auto.py,sha256=7aT4l93zAPWIuIWjqxnLpXyJJZ349CCPqPGS-yXJxho,19486
|
|
3
|
+
pythonic_fp/circulararray/fixed.py,sha256=usGVbw2wy4idhhMSZq_DFzROTrXUeiCjVpMYfetXiWs,15456
|
|
4
|
+
pythonic_fp/circulararray/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
pythonic_fp_circulararray-6.0.0.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
|
|
6
|
+
pythonic_fp_circulararray-6.0.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
7
|
+
pythonic_fp_circulararray-6.0.0.dist-info/METADATA,sha256=W9GQEAUwxQi-yzQU2yGqhanDDXYe-piRV5ZxZJYpYpk,2761
|
|
8
|
+
pythonic_fp_circulararray-6.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
from collections.abc import Callable, Iterable, Iterator
|
|
2
|
-
from typing import overload
|
|
3
|
-
|
|
4
|
-
__all__ = ['CA', 'ca']
|
|
5
|
-
|
|
6
|
-
class CA[I]:
|
|
7
|
-
def __init__(self, items: Iterable[I] | None = None) -> None: ...
|
|
8
|
-
def __iter__(self) -> Iterator[I]: ...
|
|
9
|
-
def __reversed__(self) -> Iterator[I]: ...
|
|
10
|
-
def __bool__(self) -> bool: ...
|
|
11
|
-
def __len__(self) -> int: ...
|
|
12
|
-
@overload
|
|
13
|
-
def __getitem__(self, idx: int) -> I: ...
|
|
14
|
-
@overload
|
|
15
|
-
def __getitem__(self, idx: slice) -> CA[I]: ...
|
|
16
|
-
@overload
|
|
17
|
-
def __setitem__(self, idx: int, vals: I) -> None: ...
|
|
18
|
-
@overload
|
|
19
|
-
def __setitem__(self, idx: slice, vals: Iterable[I]) -> None: ...
|
|
20
|
-
@overload
|
|
21
|
-
def __delitem__(self, idx: int) -> None: ...
|
|
22
|
-
@overload
|
|
23
|
-
def __delitem__(self, idx: slice) -> None: ...
|
|
24
|
-
def __eq__(self, other: object) -> bool: ...
|
|
25
|
-
def pushl(self, *items: I) -> None: ...
|
|
26
|
-
def pushr(self, *items: I) -> None: ...
|
|
27
|
-
def popl(self) -> I: ...
|
|
28
|
-
def popr(self) -> I: ...
|
|
29
|
-
def popld(self, default: I) -> I: ...
|
|
30
|
-
def poprd(self, default: I) -> I: ...
|
|
31
|
-
def poplt(self, maximum: int) -> tuple[I, ...]: ...
|
|
32
|
-
def poprt(self, maximum: int) -> tuple[I, ...]: ...
|
|
33
|
-
def rotl(self, n: int = 1) -> None: ...
|
|
34
|
-
def rotr(self, n: int = 1) -> None: ...
|
|
35
|
-
def map[U](self, f: Callable[[I], U]) -> CA[U]: ...
|
|
36
|
-
def foldl[L](self, f: Callable[[L, I], L], start: L | None = None) -> L: ...
|
|
37
|
-
def foldr[R](self, f: Callable[[I, R], R], start: R | None = None) -> R: ...
|
|
38
|
-
def capacity(self) -> int: ...
|
|
39
|
-
def empty(self) -> None: ...
|
|
40
|
-
def fraction_filled(self) -> float: ...
|
|
41
|
-
def resize(self, minimum_capacity: int = 2) -> None: ...
|
|
42
|
-
|
|
43
|
-
def ca[T](*items: T) -> CA[T]: ...
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
from collections.abc import Callable, Iterable, Iterator
|
|
2
|
-
|
|
3
|
-
__all__ = ['CAF', 'caf']
|
|
4
|
-
|
|
5
|
-
class CAF[I]:
|
|
6
|
-
def __init__(self, items: Iterable[I] | None = None, capacity: int = 2) -> None: ...
|
|
7
|
-
def __iter__(self) -> Iterator[I]: ...
|
|
8
|
-
def __reversed__(self) -> Iterator[I]: ...
|
|
9
|
-
def __bool__(self) -> bool: ...
|
|
10
|
-
def __len__(self) -> int: ...
|
|
11
|
-
def __getitem__(self, idx: int) -> I: ...
|
|
12
|
-
def __setitem__(self, idx: int, val: I) -> None: ...
|
|
13
|
-
def __delitem__(self, idx: int) -> None: ...
|
|
14
|
-
def __eq__(self, other: object) -> bool: ...
|
|
15
|
-
def pushl(self, item: I) -> None: ...
|
|
16
|
-
def pushr(self, item: I) -> None: ...
|
|
17
|
-
def popl(self) -> I: ...
|
|
18
|
-
def popr(self) -> I: ...
|
|
19
|
-
def popld(self, default: I) -> I: ...
|
|
20
|
-
def poprd(self, default: I) -> I: ...
|
|
21
|
-
def poplt(self, maximum: int) -> tuple[I, ...]: ...
|
|
22
|
-
def poprt(self, maximum: int) -> tuple[I, ...]: ...
|
|
23
|
-
def rotl(self, n: int = 1) -> None: ...
|
|
24
|
-
def rotr(self, n: int = 1) -> None: ...
|
|
25
|
-
def map[U](self, f: Callable[[I], U]) -> CAF[U]: ...
|
|
26
|
-
def foldl[L](self, f: Callable[[L, I], L], start: L | None = None) -> L: ...
|
|
27
|
-
def foldr[R](self, f: Callable[[I, R], R], start: R | None = None) -> R: ...
|
|
28
|
-
def capacity(self) -> int: ...
|
|
29
|
-
def empty(self) -> None: ...
|
|
30
|
-
def fraction_filled(self) -> float: ...
|
|
31
|
-
|
|
32
|
-
def caf[T](*items: T, capacity: int = 2) -> CAF[T]: ...
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
pythonic_fp/circulararray/__init__.py,sha256=uR0KASmw1Zraz9l3VPbqPm9WUKzWk-0_tUGT0opabzQ,1515
|
|
2
|
-
pythonic_fp/circulararray/__init__.pyi,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
pythonic_fp/circulararray/auto.py,sha256=_rZhOsC3PGs97j15qM4vXpkpRDTfVkxEGwYyBi9uAQU,19334
|
|
4
|
-
pythonic_fp/circulararray/auto.pyi,sha256=yfQ2kzIiXFb2Bpg3cMwHwv2oSlyOUMTgmdPZM_-oU_8,1669
|
|
5
|
-
pythonic_fp/circulararray/fixed.py,sha256=nzwPnaVOh-KKv503_c--f_m0SkOaUI6mSe1Bo5WACCA,15316
|
|
6
|
-
pythonic_fp/circulararray/fixed.pyi,sha256=y_7eMvH18B1lpLPp2P-VkN-xcQVKJn6cOisp9LSBBRk,1362
|
|
7
|
-
pythonic_fp/circulararray/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
pythonic_fp_circulararray-5.3.3.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
|
|
9
|
-
pythonic_fp_circulararray-5.3.3.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
10
|
-
pythonic_fp_circulararray-5.3.3.dist-info/METADATA,sha256=rdgGmosD6wF1Cfye7QBuZJXGnjs3uDgPzmYYvVpUx-M,2180
|
|
11
|
-
pythonic_fp_circulararray-5.3.3.dist-info/RECORD,,
|
{pythonic_fp_circulararray-5.3.3.dist-info → pythonic_fp_circulararray-6.0.0.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|