pythonic-fp.iterables 3.0.0__py3-none-any.whl → 4.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/iterables.py +345 -147
- {pythonic_fp_iterables-3.0.0.dist-info → pythonic_fp_iterables-4.0.0.dist-info}/METADATA +17 -12
- pythonic_fp_iterables-4.0.0.dist-info/RECORD +5 -0
- pythonic_fp_iterables-3.0.0.dist-info/RECORD +0 -5
- {pythonic_fp_iterables-3.0.0.dist-info → pythonic_fp_iterables-4.0.0.dist-info}/WHEEL +0 -0
- {pythonic_fp_iterables-3.0.0.dist-info → pythonic_fp_iterables-4.0.0.dist-info}/licenses/LICENSE +0 -0
pythonic_fp/iterables.py
CHANGED
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
"""
|
|
16
|
+
Library of functions for iterating iterables.
|
|
17
|
+
---------------------------------------------
|
|
18
18
|
|
|
19
19
|
- Concatenating and merging iterables
|
|
20
20
|
- Dropping and taking values from iterables
|
|
@@ -26,6 +26,7 @@ Library of iterator related functions and enumerations.
|
|
|
26
26
|
|
|
27
27
|
- all iterators are assumed to be iterable
|
|
28
28
|
- for all iterators ``foo`` we assume ``iter(foo) is foo``
|
|
29
|
+
|
|
29
30
|
"""
|
|
30
31
|
|
|
31
32
|
from __future__ import annotations
|
|
@@ -36,9 +37,9 @@ __license__ = 'Apache License 2.0'
|
|
|
36
37
|
|
|
37
38
|
from collections.abc import Callable, Iterable, Iterator
|
|
38
39
|
from enum import auto, Enum
|
|
39
|
-
from typing import cast, Never
|
|
40
|
+
from typing import cast, Never
|
|
40
41
|
from pythonic_fp.containers.box import Box
|
|
41
|
-
from pythonic_fp.containers.maybe import MayBe
|
|
42
|
+
from pythonic_fp.containers.maybe import MayBe
|
|
42
43
|
from pythonic_fp.fptools.function import negate, swap
|
|
43
44
|
from pythonic_fp.fptools.singletons import NoValue
|
|
44
45
|
|
|
@@ -54,27 +55,24 @@ __all__ = [
|
|
|
54
55
|
'take_split',
|
|
55
56
|
'take_while_split',
|
|
56
57
|
'accumulate',
|
|
57
|
-
'foldl',
|
|
58
|
-
'mb_fold_left',
|
|
59
58
|
'reduce_left',
|
|
59
|
+
'fold_left',
|
|
60
|
+
'maybe_fold_left',
|
|
60
61
|
'sc_reduce_left',
|
|
61
62
|
'sc_reduce_right',
|
|
62
63
|
]
|
|
63
64
|
|
|
64
|
-
D = TypeVar('D') # Needed only for pdoc documentation generation.
|
|
65
|
-
L = TypeVar('L') # Otherwise, ignored by both MyPy and Python.
|
|
66
|
-
|
|
67
65
|
# Iterate over multiple iterables
|
|
68
66
|
|
|
69
67
|
|
|
70
68
|
class FM(Enum):
|
|
71
|
-
"""
|
|
69
|
+
"""Iterable Blending Enums.
|
|
72
70
|
|
|
73
|
-
-
|
|
74
|
-
-
|
|
75
|
-
-
|
|
76
|
-
"""
|
|
71
|
+
- *CONCAT:* Concatenate iterables first to last
|
|
72
|
+
- *MERGE:* Merge iterables until one is exhausted
|
|
73
|
+
- *EXHAUST:* Merge iterables until all are exhausted
|
|
77
74
|
|
|
75
|
+
"""
|
|
78
76
|
CONCAT = auto()
|
|
79
77
|
MERGE = auto()
|
|
80
78
|
EXHAUST = auto()
|
|
@@ -83,10 +81,18 @@ class FM(Enum):
|
|
|
83
81
|
def concat[D](*iterables: Iterable[D]) -> Iterator[D]:
|
|
84
82
|
"""Sequentially concatenate multiple iterables together.
|
|
85
83
|
|
|
84
|
+
.. code:: python
|
|
85
|
+
|
|
86
|
+
def concat[D](iterable: Iterable[D]) -> Iterator[D]
|
|
87
|
+
|
|
86
88
|
- pure Python version of standard library's ``itertools.chain``
|
|
87
89
|
- iterator sequentially yields each iterable until all are exhausted
|
|
88
90
|
- an infinite iterable will prevent subsequent iterables from yielding any values
|
|
89
91
|
- performant to ``itertools.chain``
|
|
92
|
+
|
|
93
|
+
:param iterables: iterables to concatenate
|
|
94
|
+
:return: concatenated iterators
|
|
95
|
+
|
|
90
96
|
"""
|
|
91
97
|
for iterator in map(lambda x: iter(x), iterables):
|
|
92
98
|
while True:
|
|
@@ -97,9 +103,47 @@ def concat[D](*iterables: Iterable[D]) -> Iterator[D]:
|
|
|
97
103
|
break
|
|
98
104
|
|
|
99
105
|
|
|
106
|
+
def merge[D](*iterables: Iterable[D], yield_partials: bool = False) -> Iterator[D]:
|
|
107
|
+
"""Shuffle together ``iterables`` until one is exhausted.
|
|
108
|
+
|
|
109
|
+
.. code:: python
|
|
110
|
+
|
|
111
|
+
def merge[D](iterable: Iterable[D], yield_partials: bool) -> Iterator[D]
|
|
112
|
+
|
|
113
|
+
If ``yield_partials`` is true
|
|
114
|
+
|
|
115
|
+
- yield any unmatched yielded values from other iterables
|
|
116
|
+
- prevents data lose if any of the iterables are iterators with external references
|
|
117
|
+
|
|
118
|
+
:param iterables: iterables to merge until one gets exhausted
|
|
119
|
+
:return: merged iterators
|
|
120
|
+
|
|
121
|
+
"""
|
|
122
|
+
iter_list = list(map(lambda x: iter(x), iterables))
|
|
123
|
+
values = []
|
|
124
|
+
if (num_iters := len(iter_list)) > 0:
|
|
125
|
+
while True:
|
|
126
|
+
try:
|
|
127
|
+
for ii in range(num_iters):
|
|
128
|
+
values.append(next(iter_list[ii]))
|
|
129
|
+
yield from values
|
|
130
|
+
values.clear()
|
|
131
|
+
except StopIteration:
|
|
132
|
+
break
|
|
133
|
+
if yield_partials:
|
|
134
|
+
yield from values
|
|
135
|
+
|
|
136
|
+
|
|
100
137
|
def exhaust[D](*iterables: Iterable[D]) -> Iterator[D]:
|
|
101
138
|
"""Shuffle together multiple iterables until all are exhausted.
|
|
102
|
-
|
|
139
|
+
|
|
140
|
+
.. code:: python
|
|
141
|
+
|
|
142
|
+
def exhaust[D](iterable: Iterable[D]) -> Iterator[D]
|
|
143
|
+
|
|
144
|
+
:param iterables: iterables to exhaustively merge
|
|
145
|
+
:return: merged iterators
|
|
146
|
+
|
|
103
147
|
"""
|
|
104
148
|
iter_list = list(map(lambda x: iter(x), iterables))
|
|
105
149
|
if (num_iters := len(iter_list)) > 0:
|
|
@@ -122,37 +166,24 @@ def exhaust[D](*iterables: Iterable[D]) -> Iterator[D]:
|
|
|
122
166
|
yield from values
|
|
123
167
|
|
|
124
168
|
|
|
125
|
-
|
|
126
|
-
"""Shuffle together the ``iterables`` until one is exhausted.
|
|
169
|
+
## dropping and taking
|
|
127
170
|
|
|
128
|
-
- iterator yields until one of the iterables is exhausted
|
|
129
|
-
- if ``yield_partials`` is true,
|
|
130
171
|
|
|
131
|
-
|
|
132
|
-
|
|
172
|
+
def drop[D](iterable: Iterable[D], n: int) -> Iterator[D]:
|
|
173
|
+
"""Drop the next n values from iterable.
|
|
133
174
|
|
|
134
|
-
|
|
135
|
-
"""
|
|
136
|
-
iter_list = list(map(lambda x: iter(x), iterables))
|
|
137
|
-
values = []
|
|
138
|
-
if (num_iters := len(iter_list)) > 0:
|
|
139
|
-
while True:
|
|
140
|
-
try:
|
|
141
|
-
for ii in range(num_iters):
|
|
142
|
-
values.append(next(iter_list[ii]))
|
|
143
|
-
yield from values
|
|
144
|
-
values.clear()
|
|
145
|
-
except StopIteration:
|
|
146
|
-
break
|
|
147
|
-
if yield_partials:
|
|
148
|
-
yield from values
|
|
175
|
+
.. code:: python
|
|
149
176
|
|
|
177
|
+
def drop[D](
|
|
178
|
+
iterable: Iterable[D],
|
|
179
|
+
n: int
|
|
180
|
+
) -> Iterator[D]
|
|
150
181
|
|
|
151
|
-
|
|
152
|
-
|
|
182
|
+
:param iterable: iterable whose values are to be dropped
|
|
183
|
+
:param n: number of values to be dropped
|
|
184
|
+
:return: an iterator of the remaining values
|
|
153
185
|
|
|
154
|
-
|
|
155
|
-
"""Drop the next ``n`` values from ``iterable``."""
|
|
186
|
+
"""
|
|
156
187
|
it = iter(iterable)
|
|
157
188
|
for _ in range(n):
|
|
158
189
|
try:
|
|
@@ -162,8 +193,21 @@ def drop[D](iterable: Iterable[D], n: int, /) -> Iterator[D]:
|
|
|
162
193
|
return it
|
|
163
194
|
|
|
164
195
|
|
|
165
|
-
def drop_while[D](iterable: Iterable[D], pred: Callable[[D], bool]
|
|
166
|
-
"""Drop initial values from
|
|
196
|
+
def drop_while[D](iterable: Iterable[D], pred: Callable[[D], bool]) -> Iterator[D]:
|
|
197
|
+
"""Drop initial values from iterable while predicate is true.
|
|
198
|
+
|
|
199
|
+
.. code:: python
|
|
200
|
+
|
|
201
|
+
def drop_while[D](
|
|
202
|
+
iterable: Iterable[D],
|
|
203
|
+
pred: Callable[[D], bool]
|
|
204
|
+
) -> Iterator[D]
|
|
205
|
+
|
|
206
|
+
:param iterable: iterable whose values are to be dropped
|
|
207
|
+
:param pred: Boolean valued function
|
|
208
|
+
:return: an iterator beginning where pred returned false
|
|
209
|
+
|
|
210
|
+
"""
|
|
167
211
|
it = iter(iterable)
|
|
168
212
|
while True:
|
|
169
213
|
try:
|
|
@@ -176,8 +220,21 @@ def drop_while[D](iterable: Iterable[D], pred: Callable[[D], bool], /) -> Iterat
|
|
|
176
220
|
return it
|
|
177
221
|
|
|
178
222
|
|
|
179
|
-
def take[D](iterable: Iterable[D], n: int
|
|
180
|
-
"""Return an iterator of up to
|
|
223
|
+
def take[D](iterable: Iterable[D], n: int) -> Iterator[D]:
|
|
224
|
+
"""Return an iterator of up to n initial values of an iterable.
|
|
225
|
+
|
|
226
|
+
.. code:: python
|
|
227
|
+
|
|
228
|
+
def take[D](
|
|
229
|
+
iterable: Iterable[D],
|
|
230
|
+
n: int
|
|
231
|
+
) -> Iterator[D]
|
|
232
|
+
|
|
233
|
+
:param iterable: iterable providing the values to be taken
|
|
234
|
+
:param n: number of values to be dropped
|
|
235
|
+
:return: an iterator of up to n initial values from iterable
|
|
236
|
+
|
|
237
|
+
"""
|
|
181
238
|
it = iter(iterable)
|
|
182
239
|
for _ in range(n):
|
|
183
240
|
try:
|
|
@@ -187,27 +244,23 @@ def take[D](iterable: Iterable[D], n: int, /) -> Iterator[D]:
|
|
|
187
244
|
break
|
|
188
245
|
|
|
189
246
|
|
|
190
|
-
def
|
|
191
|
-
"""
|
|
192
|
-
|
|
193
|
-
- return a tuple of
|
|
194
|
-
|
|
195
|
-
- an iterator of up to ``n`` initial values
|
|
196
|
-
- an iterator of the remaining vales of the ``iterable``
|
|
197
|
-
|
|
198
|
-
- Contract: do not access second iterator until first is exhausted
|
|
199
|
-
"""
|
|
200
|
-
it = iter(iterable)
|
|
201
|
-
itn = take(it, n)
|
|
202
|
-
|
|
203
|
-
return itn, it
|
|
247
|
+
def take_while[D](iterable: Iterable[D], pred: Callable[[D], bool]) -> Iterator[D]:
|
|
248
|
+
"""Yield values from iterable while predicate is true.
|
|
204
249
|
|
|
250
|
+
.. code:: python
|
|
205
251
|
|
|
206
|
-
def take_while[D](
|
|
207
|
-
|
|
252
|
+
def take_while[D](
|
|
253
|
+
iterable: Iterable[D],
|
|
254
|
+
pred: Callable[[D], bool]
|
|
255
|
+
) -> Iterator[D]
|
|
208
256
|
|
|
209
257
|
.. warning::
|
|
210
258
|
Risk of value loss if iterable is multiple referenced iterator.
|
|
259
|
+
|
|
260
|
+
:param iterable: iterable providing the values to be taken
|
|
261
|
+
:param pred: Boolean valued function
|
|
262
|
+
:return: an Iterator of up to n initial values from the iterable
|
|
263
|
+
|
|
211
264
|
"""
|
|
212
265
|
it = iter(iterable)
|
|
213
266
|
while True:
|
|
@@ -221,19 +274,54 @@ def take_while[D](iterable: Iterable[D], pred: Callable[[D], bool], /) -> Iterat
|
|
|
221
274
|
break
|
|
222
275
|
|
|
223
276
|
|
|
277
|
+
def take_split[D](iterable: Iterable[D], n: int) -> tuple[Iterator[D], Iterator[D]]:
|
|
278
|
+
"""Same as take except also return iterator of remaining values.
|
|
279
|
+
|
|
280
|
+
.. code:: python
|
|
281
|
+
|
|
282
|
+
def take_split[D](
|
|
283
|
+
iterable: Iterable[D],
|
|
284
|
+
n: int
|
|
285
|
+
) -> tuple[Iterator[D], Iterator[D]]
|
|
286
|
+
|
|
287
|
+
.. Warning::
|
|
288
|
+
|
|
289
|
+
**CONTRACT:** Do not access the second returned iterator until the
|
|
290
|
+
first one is exhausted.
|
|
291
|
+
|
|
292
|
+
:param iterable: iterable providing the values to be taken
|
|
293
|
+
:param n: maximum number of values to be taken
|
|
294
|
+
:return: an iterator of values taken and an iterator of remaining values
|
|
295
|
+
|
|
296
|
+
"""
|
|
297
|
+
it = iter(iterable)
|
|
298
|
+
itn = take(it, n)
|
|
299
|
+
|
|
300
|
+
return itn, it
|
|
301
|
+
|
|
302
|
+
|
|
224
303
|
def take_while_split[D](
|
|
225
|
-
iterable: Iterable[D], pred: Callable[[D], bool]
|
|
304
|
+
iterable: Iterable[D], pred: Callable[[D], bool]
|
|
226
305
|
) -> tuple[Iterator[D], Iterator[D]]:
|
|
227
|
-
"""Yield values from
|
|
306
|
+
"""Yield values from iterable while predicate is true.
|
|
228
307
|
|
|
229
|
-
|
|
308
|
+
.. code:: python
|
|
230
309
|
|
|
231
|
-
|
|
232
|
-
|
|
310
|
+
def take_while_split[D](
|
|
311
|
+
iterable: Iterable[D],
|
|
312
|
+
pred: Callable[[D], bool]
|
|
313
|
+
) -> tuple[Iterator[D], Iterator[D]]
|
|
233
314
|
|
|
234
|
-
|
|
235
|
-
"""
|
|
315
|
+
.. Warning::
|
|
236
316
|
|
|
317
|
+
**CONTRACT:** Do not access the second returned iterator until
|
|
318
|
+
the first one is exhausted.
|
|
319
|
+
|
|
320
|
+
:param iterable: iterable providing the values to be taken
|
|
321
|
+
:param pred: Boolean valued function
|
|
322
|
+
:return: an iterator of values taken and an iterator of remaining values
|
|
323
|
+
|
|
324
|
+
"""
|
|
237
325
|
def _take_while(
|
|
238
326
|
it: Iterator[D], pred: Callable[[D], bool], val: Box[D]
|
|
239
327
|
) -> Iterator[D]:
|
|
@@ -258,13 +346,30 @@ def take_while_split[D](
|
|
|
258
346
|
|
|
259
347
|
|
|
260
348
|
def accumulate[D, L](
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
349
|
+
iterable: Iterable[D],
|
|
350
|
+
f: Callable[[L, D], L],
|
|
351
|
+
initial: L | NoValue = NoValue()
|
|
352
|
+
) -> Iterator[L]:
|
|
353
|
+
"""Returns an iterator of partial fold values.
|
|
354
|
+
|
|
355
|
+
.. code:: python
|
|
356
|
+
|
|
357
|
+
def accumulate[D, L](
|
|
358
|
+
iterable: Iterable[D],
|
|
359
|
+
f: Callable[[L, D], L],
|
|
360
|
+
initial: L
|
|
361
|
+
) -> Iterator[L]
|
|
362
|
+
|
|
363
|
+
A pure Python version of standard library's ``itertools.accumulate``
|
|
264
364
|
|
|
265
|
-
- pure Python version of standard library's ``itertools.accumulate``
|
|
266
365
|
- function ``f`` does not default to addition (for typing flexibility)
|
|
267
|
-
- begins accumulation with an optional ``initial`` value
|
|
366
|
+
- begins accumulation with an "optional" ``initial`` value
|
|
367
|
+
|
|
368
|
+
:param iterable: iterable to be folded
|
|
369
|
+
:param f: 2 parameter function, first parameter for accumulated value
|
|
370
|
+
:param initial: "optional" initial value to start fold
|
|
371
|
+
:return: an iterator of the intermediate fold values
|
|
372
|
+
|
|
268
373
|
"""
|
|
269
374
|
it = iter(iterable)
|
|
270
375
|
try:
|
|
@@ -290,19 +395,36 @@ def accumulate[D, L](
|
|
|
290
395
|
yield acc
|
|
291
396
|
|
|
292
397
|
|
|
293
|
-
def reduce_left[D](iterable: Iterable[D], f: Callable[[D, D], D]
|
|
398
|
+
def reduce_left[D](iterable: Iterable[D], f: Callable[[D, D], D]) -> D | Never:
|
|
294
399
|
"""Fold an iterable left with a function.
|
|
295
400
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
401
|
+
.. code:: python
|
|
402
|
+
|
|
403
|
+
def reduce_left[D](
|
|
404
|
+
iterable: Iterable[D],
|
|
405
|
+
f: Callable[[D, D], D]
|
|
406
|
+
) -> D | Never
|
|
407
|
+
|
|
408
|
+
.. Warning::
|
|
409
|
+
|
|
410
|
+
This function never return if given an infinite iterable.
|
|
411
|
+
|
|
412
|
+
.. Warning::
|
|
413
|
+
|
|
414
|
+
This function does not catch or re-raises exceptions from ``f``.
|
|
415
|
+
|
|
416
|
+
:param iterable: iterable to be reduced (folded)
|
|
417
|
+
:param f: 2 parameter function, first parameter for accumulated value
|
|
418
|
+
:return: reduced value from the iterable
|
|
419
|
+
:raises StopIteration: when called on an empty circular array
|
|
420
|
+
:raises Exception: does not catch or re-raises exceptions from ``f``.
|
|
421
|
+
|
|
300
422
|
"""
|
|
301
423
|
it = iter(iterable)
|
|
302
424
|
try:
|
|
303
425
|
acc = next(it)
|
|
304
426
|
except StopIteration as exc:
|
|
305
|
-
msg = '
|
|
427
|
+
msg = 'Attempt to reduce an empty iterable?'
|
|
306
428
|
raise StopIteration(msg) from exc
|
|
307
429
|
|
|
308
430
|
for v in it:
|
|
@@ -311,20 +433,38 @@ def reduce_left[D](iterable: Iterable[D], f: Callable[[D, D], D], /) -> D | Neve
|
|
|
311
433
|
return acc
|
|
312
434
|
|
|
313
435
|
|
|
314
|
-
def
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
436
|
+
def fold_left[D, L](
|
|
437
|
+
iterable: Iterable[D],
|
|
438
|
+
f: Callable[[L, D], L],
|
|
439
|
+
initial: L
|
|
440
|
+
) -> L | Never:
|
|
441
|
+
"""Fold an iterable left with a function and initial value.
|
|
318
442
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
443
|
+
.. code:: python
|
|
444
|
+
|
|
445
|
+
def fold_left[D, L](
|
|
446
|
+
iterable: Iterable[D],
|
|
447
|
+
f: Callable[[L, D], L],
|
|
448
|
+
initial: L
|
|
449
|
+
) -> L | Never
|
|
322
450
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
451
|
+
- not restricted to ``__add__`` for the folding function
|
|
452
|
+
- initial value required, does not default to ``0`` for initial value
|
|
453
|
+
- handles non-numeric data just find
|
|
454
|
+
|
|
455
|
+
.. Warning::
|
|
456
|
+
|
|
457
|
+
This function never return if given an infinite iterable.
|
|
458
|
+
|
|
459
|
+
.. Warning::
|
|
460
|
+
|
|
461
|
+
This function does not catch any exceptions ``f`` may raise.
|
|
462
|
+
|
|
463
|
+
:param iterable: iterable to be folded
|
|
464
|
+
:param f: 2 parameter function, first parameter for accumulated value
|
|
465
|
+
:param initial: mandatory initial value to start fold
|
|
466
|
+
:return: the folded value
|
|
326
467
|
|
|
327
|
-
- never returns if ``iterable`` generates an infinite iterator
|
|
328
468
|
"""
|
|
329
469
|
acc = initial
|
|
330
470
|
for v in iterable:
|
|
@@ -332,15 +472,38 @@ def foldl[D, L](
|
|
|
332
472
|
return acc
|
|
333
473
|
|
|
334
474
|
|
|
335
|
-
def
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
475
|
+
def maybe_fold_left[D, L](
|
|
476
|
+
iterable: Iterable[D],
|
|
477
|
+
f: Callable[[L, D], L],
|
|
478
|
+
initial: L | NoValue = NoValue()
|
|
479
|
+
) -> MayBe[L] | Never:
|
|
480
|
+
"""Folds an iterable left with an "optional" initial value..
|
|
481
|
+
|
|
482
|
+
.. code:: python
|
|
483
|
+
|
|
484
|
+
def maybe_fold_left[D, L](
|
|
485
|
+
iterable: Iterable[D],
|
|
486
|
+
f: Callable[[L, D], L],
|
|
487
|
+
initial: L
|
|
488
|
+
) -> MayBe[L] | Never
|
|
339
489
|
|
|
340
490
|
- traditional FP type order given for function ``f``
|
|
341
|
-
- when an initial value is not given then
|
|
342
|
-
- if iterable empty and no ``initial`` value given, return ``
|
|
343
|
-
|
|
491
|
+
- when an initial value is not given then ``L = D``
|
|
492
|
+
- if iterable empty and no ``initial`` value given, return ``MayBe()``
|
|
493
|
+
|
|
494
|
+
.. Warning::
|
|
495
|
+
|
|
496
|
+
This function never return if given an infinite iterable.
|
|
497
|
+
|
|
498
|
+
.. Warning::
|
|
499
|
+
|
|
500
|
+
This function does not catch any exceptions ``f`` may raise.
|
|
501
|
+
|
|
502
|
+
:param iterable: The iterable to be folded.
|
|
503
|
+
:param f: First argument is for the accumulated value.
|
|
504
|
+
:param initial: Mandatory initial value to start fold.
|
|
505
|
+
:return: Folded value if it exists.
|
|
506
|
+
|
|
344
507
|
"""
|
|
345
508
|
acc: L
|
|
346
509
|
it = iter(iterable)
|
|
@@ -348,7 +511,7 @@ def mb_fold_left[L, D](
|
|
|
348
511
|
try:
|
|
349
512
|
acc = cast(L, next(it)) # in this case L = D
|
|
350
513
|
except StopIteration:
|
|
351
|
-
return
|
|
514
|
+
return MayBe()
|
|
352
515
|
else:
|
|
353
516
|
acc = cast(L, initial)
|
|
354
517
|
|
|
@@ -356,30 +519,48 @@ def mb_fold_left[L, D](
|
|
|
356
519
|
try:
|
|
357
520
|
acc = f(acc, v)
|
|
358
521
|
except Exception:
|
|
359
|
-
return
|
|
522
|
+
return MayBe()
|
|
360
523
|
|
|
361
|
-
return
|
|
524
|
+
return MayBe(acc)
|
|
362
525
|
|
|
363
526
|
|
|
364
527
|
def sc_reduce_left[D](
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
528
|
+
iterable: Iterable[D],
|
|
529
|
+
f: Callable[[D, D], D],
|
|
530
|
+
start: Callable[[D], bool] = (lambda d: True),
|
|
531
|
+
stop: Callable[[D], bool] = (lambda d: False),
|
|
532
|
+
include_start: bool = True,
|
|
533
|
+
include_stop: bool = True,
|
|
534
|
+
) -> tuple[MayBe[D], Iterator[D]]:
|
|
535
|
+
"""Short circuit version of a left reduce.
|
|
536
|
+
|
|
537
|
+
.. code:: python
|
|
538
|
+
|
|
539
|
+
def sc_reduce_left[D](
|
|
540
|
+
iterable: Iterable[D],
|
|
541
|
+
f: Callable[[D, D], D],
|
|
542
|
+
start: Callable[[D], bool],
|
|
543
|
+
stop: Callable[[D], bool],
|
|
544
|
+
include_start: bool,
|
|
545
|
+
include_stop: bool
|
|
546
|
+
) -> tuple[MayBe[D], Iterator[D]]
|
|
547
|
+
|
|
548
|
+
Useful for infinite iterables.
|
|
549
|
+
|
|
550
|
+
Behavior for default arguments will
|
|
551
|
+
|
|
552
|
+
- left reduce finite iterable
|
|
553
|
+
- start folding immediately
|
|
554
|
+
- continue folding until end (of a possibly infinite iterable)
|
|
555
|
+
|
|
556
|
+
:param iterable: iterable to be reduced from the left
|
|
557
|
+
:param f: 2 parameter function, first parameter for accumulated value
|
|
558
|
+
:param start: delay starting the fold until it returns true
|
|
559
|
+
:param stop: prematurely stop the fold when it returns true
|
|
560
|
+
:param include_start: if true, include fold starting value in fold
|
|
561
|
+
:param include_stop: if true, include stopping value in fold
|
|
562
|
+
:return: MayBe of the folded value and remaining iterables
|
|
563
|
+
|
|
383
564
|
"""
|
|
384
565
|
it_start = drop_while(iterable, negate(start))
|
|
385
566
|
if not include_start:
|
|
@@ -388,18 +569,18 @@ def sc_reduce_left[D](
|
|
|
388
569
|
except StopIteration:
|
|
389
570
|
pass
|
|
390
571
|
it_reduce, it_rest = take_while_split(it_start, negate(stop))
|
|
391
|
-
mb_reduced =
|
|
572
|
+
mb_reduced = maybe_fold_left(it_reduce, f)
|
|
392
573
|
if include_stop:
|
|
393
574
|
if mb_reduced:
|
|
394
575
|
try:
|
|
395
576
|
last = next(it_rest)
|
|
396
|
-
mb_reduced =
|
|
577
|
+
mb_reduced = MayBe(f(mb_reduced.get(), last))
|
|
397
578
|
except StopIteration:
|
|
398
579
|
pass
|
|
399
580
|
else:
|
|
400
581
|
try:
|
|
401
582
|
last = next(it_rest)
|
|
402
|
-
mb_reduced =
|
|
583
|
+
mb_reduced = MayBe(last)
|
|
403
584
|
except StopIteration:
|
|
404
585
|
pass
|
|
405
586
|
|
|
@@ -407,40 +588,57 @@ def sc_reduce_left[D](
|
|
|
407
588
|
|
|
408
589
|
|
|
409
590
|
def sc_reduce_right[D](
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
591
|
+
iterable: Iterable[D],
|
|
592
|
+
f: Callable[[D, D], D],
|
|
593
|
+
start: Callable[[D], bool] = (lambda d: False),
|
|
594
|
+
stop: Callable[[D], bool] = (lambda d: False),
|
|
595
|
+
include_start: bool = True,
|
|
596
|
+
include_stop: bool = True,
|
|
597
|
+
) -> tuple[MayBe[D], Iterator[D]]:
|
|
598
|
+
"""Short circuit version of a right reduce.
|
|
599
|
+
|
|
600
|
+
.. code:: python
|
|
601
|
+
|
|
602
|
+
def sc_reduce_left[D](
|
|
603
|
+
iterable: Iterable[D],
|
|
604
|
+
f: Callable[[D, D], D],
|
|
605
|
+
start: Callable[[D], bool],
|
|
606
|
+
stop: Callable[[D], bool],
|
|
607
|
+
include_start: bool,
|
|
608
|
+
include_stop: bool
|
|
609
|
+
) -> Tuple[MayBe[D] | Iterator[D]
|
|
610
|
+
|
|
611
|
+
Useful for infinite and non-reversible iterables.
|
|
612
|
+
|
|
613
|
+
Behavior for default arguments will
|
|
614
|
+
|
|
615
|
+
- right reduce finite iterable
|
|
616
|
+
- start folding at end (of a possibly infinite iterable)
|
|
617
|
+
- continue reducing right until beginning
|
|
618
|
+
|
|
619
|
+
:param iterable: iterable to be reduced from the right
|
|
620
|
+
:param f: 2 parameter function, second parameter for accumulated value
|
|
621
|
+
:param start: delay starting the fold until it returns true
|
|
622
|
+
:param stop: prematurely stop the fold when it returns true
|
|
623
|
+
:param include_start: if true, include fold starting value in fold
|
|
624
|
+
:param include_stop: if true, include stopping value in fold
|
|
625
|
+
:return: MayBe of the folded value and remaining iterables
|
|
626
|
+
|
|
429
627
|
"""
|
|
430
628
|
it_start, it_rest = take_while_split(iterable, negate(start))
|
|
431
|
-
|
|
629
|
+
list1 = list(it_start)
|
|
432
630
|
if include_start:
|
|
433
631
|
try:
|
|
434
632
|
begin = next(it_rest)
|
|
435
633
|
except StopIteration:
|
|
436
634
|
pass
|
|
437
635
|
else:
|
|
438
|
-
|
|
636
|
+
list1.append(begin)
|
|
439
637
|
|
|
440
|
-
|
|
441
|
-
it_reduce, it_stop = take_while_split(
|
|
638
|
+
list1.reverse()
|
|
639
|
+
it_reduce, it_stop = take_while_split(list1, negate(stop))
|
|
442
640
|
|
|
443
|
-
mb_reduced =
|
|
641
|
+
mb_reduced = maybe_fold_left(it_reduce, swap(f))
|
|
444
642
|
if include_stop:
|
|
445
643
|
try:
|
|
446
644
|
end = next(it_stop)
|
|
@@ -448,8 +646,8 @@ def sc_reduce_right[D](
|
|
|
448
646
|
pass
|
|
449
647
|
else:
|
|
450
648
|
if mb_reduced:
|
|
451
|
-
mb_reduced =
|
|
649
|
+
mb_reduced = MayBe(f(end, mb_reduced.get()))
|
|
452
650
|
else:
|
|
453
|
-
mb_reduced =
|
|
651
|
+
mb_reduced = MayBe(end)
|
|
454
652
|
|
|
455
653
|
return (mb_reduced, it_rest)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pythonic-fp.iterables
|
|
3
|
-
Version:
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 4.0.0
|
|
4
|
+
Summary: Library of functions for iterating iterables.
|
|
5
5
|
Keywords: iterables,iterators
|
|
6
6
|
Author-email: "Geoffrey R. Scheller" <geoffrey@scheller.com>
|
|
7
7
|
Requires-Python: >=3.12
|
|
@@ -17,8 +17,8 @@ License-File: LICENSE
|
|
|
17
17
|
Requires-Dist: pythonic-fp.circulararray>=5.0.0
|
|
18
18
|
Requires-Dist: pythonic-fp.containers >=2.0.0
|
|
19
19
|
Requires-Dist: pythonic-fp.fptools >=3.0.0
|
|
20
|
-
Project-URL: Changelog, https://
|
|
21
|
-
Project-URL: Documentation, https://grscheller.github.io/pythonic-fp
|
|
20
|
+
Project-URL: Changelog, https://github.com/grscheller/pythonic-fp-iterables/blob/main/CHANGELOG.rst
|
|
21
|
+
Project-URL: Documentation, https://grscheller.github.io/pythonic-fp/iterables/API/development/build/html/releases.html
|
|
22
22
|
Project-URL: Homepage, https://github.com/grscheller/pythonic-fp/blob/main/README.md
|
|
23
23
|
Project-URL: Source, https://github.com/grscheller/pythonic-fp-iterables
|
|
24
24
|
|
|
@@ -26,24 +26,29 @@ Project-URL: Source, https://github.com/grscheller/pythonic-fp-iterables
|
|
|
26
26
|
|
|
27
27
|
PyPI project
|
|
28
28
|
[pythonic-fp.iterators](https://pypi.org/project/pythonic-fp.iterables/)
|
|
29
|
-
implementing a Python
|
|
29
|
+
implementing a Python library of functions for iterables and iterators.
|
|
30
30
|
|
|
31
31
|
- Concatenating and merging iterables
|
|
32
32
|
- Dropping and taking values from iterables
|
|
33
33
|
- Reducing and accumulating iterables
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
|
|
35
|
+
#### Info:
|
|
36
|
+
|
|
37
|
+
This library assumes that at all times iterator protocol
|
|
38
|
+
is followed. This means that all iterators are iterable and
|
|
39
|
+
that ``iter(some_iterator) is some_iterator``.
|
|
40
|
+
|
|
41
|
+
#### Note:
|
|
42
|
+
|
|
43
|
+
Iterables are not necessarily iterators.
|
|
39
44
|
|
|
40
45
|
This PyPI project is part of of the grscheller
|
|
41
46
|
[pythonic-fp namespace projects](https://github.com/grscheller/pythonic-fp/blob/main/README.md).
|
|
42
47
|
|
|
43
48
|
## Documentation
|
|
44
49
|
|
|
45
|
-
Documentation hosted on
|
|
46
|
-
[GitHub Pages](https://grscheller.github.io/pythonic-fp
|
|
50
|
+
Documentation for this PyPI projects is hosted on
|
|
51
|
+
[GitHub Pages](https://grscheller.github.io/pythonic-fp/iterators/API/development/build/html/releases.html).
|
|
47
52
|
|
|
48
53
|
## Copyright and License
|
|
49
54
|
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
pythonic_fp/iterables.py,sha256=PMAE7J3GWFtOqVmIlquPprP584wxMnblO3AJxaI3WAQ,18186
|
|
2
|
+
pythonic_fp_iterables-4.0.0.dist-info/licenses/LICENSE,sha256=IJMkVWQUVy-SonK6SMaMotDQ6Zb-lVi2bUTiGp1GUO8,10774
|
|
3
|
+
pythonic_fp_iterables-4.0.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
4
|
+
pythonic_fp_iterables-4.0.0.dist-info/METADATA,sha256=vOvNgxdVrs0JBSf3dSA8NaHZXiW_LEVNZ8LR51r7fUQ,2160
|
|
5
|
+
pythonic_fp_iterables-4.0.0.dist-info/RECORD,,
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
pythonic_fp/iterables.py,sha256=Si4a4JcJhrStvpxqe8msaqiskcgrb3D8xJP2oN-6VG8,13194
|
|
2
|
-
pythonic_fp_iterables-3.0.0.dist-info/licenses/LICENSE,sha256=IJMkVWQUVy-SonK6SMaMotDQ6Zb-lVi2bUTiGp1GUO8,10774
|
|
3
|
-
pythonic_fp_iterables-3.0.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
4
|
-
pythonic_fp_iterables-3.0.0.dist-info/METADATA,sha256=9vZd4-jSvYbAXdXXSo6UwZHcLgrSEprn2J6cgyqUkeA,2059
|
|
5
|
-
pythonic_fp_iterables-3.0.0.dist-info/RECORD,,
|
|
File without changes
|
{pythonic_fp_iterables-3.0.0.dist-info → pythonic_fp_iterables-4.0.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|