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 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
- """Module fp.iterables - Iterator related tools
16
-
17
- Library of iterator related functions and enumerations.
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, TypeVar
40
+ from typing import cast, Never
40
41
  from pythonic_fp.containers.box import Box
41
- from pythonic_fp.containers.maybe import MayBe as MB
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
- """Types of iterable blending,
69
+ """Iterable Blending Enums.
72
70
 
73
- - **CONCAT:** Concatenate first to last
74
- - **MERGE:** Merge until one is exhausted
75
- - **EXHAUST:** Merge until all are exhausted
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
- Iterator yields until all iterables are exhausted.
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
- def merge[D](*iterables: Iterable[D], yield_partials: bool = False) -> Iterator[D]:
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
- - yield any unmatched yielded values from other iterables
132
- - prevents data lose
172
+ def drop[D](iterable: Iterable[D], n: int) -> Iterator[D]:
173
+ """Drop the next n values from iterable.
133
174
 
134
- - if any of the iterables are iterators with external references
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
- ## dropping and taking
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
- def drop[D](iterable: Iterable[D], n: int, /) -> Iterator[D]:
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], /) -> Iterator[D]:
166
- """Drop initial values from ``iterable`` while predicate is true."""
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, /) -> Iterator[D]:
180
- """Return an iterator of up to ``n`` initial values of an iterable"""
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 take_split[D](iterable: Iterable[D], n: int, /) -> tuple[Iterator[D], Iterator[D]]:
191
- """Same as take except also return an iterator of the remaining values.
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](iterable: Iterable[D], pred: Callable[[D], bool], /) -> Iterator[D]:
207
- """Yield values from ``iterable`` while predicate is true.
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 ``iterable`` while ``predicate`` is true.
306
+ """Yield values from iterable while predicate is true.
228
307
 
229
- - return a tuple of two iterators
308
+ .. code:: python
230
309
 
231
- - first of initial values where predicate is true, followed by first to fail
232
- - second of the remaining values of the iterable after first failed value
310
+ def take_while_split[D](
311
+ iterable: Iterable[D],
312
+ pred: Callable[[D], bool]
313
+ ) -> tuple[Iterator[D], Iterator[D]]
233
314
 
234
- - **Contract:** do not access second iterator until first is exhausted
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
- iterable: Iterable[D], f: Callable[[L, D], L], initial: L | NoValue = NoValue(), /
262
- ) -> Iterator[L]:
263
- """Returns an iterator of accumulated values.
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], /) -> D | Never:
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
- - traditional FP type order given for function ``f``
297
- - if iterable empty, ``StopIteration`` exception raised
298
- - does not catch any exceptions ``f`` may raise
299
- - never returns if ``iterable`` generates an infinite iterator
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 = 'Attemped to reduce an empty iterable.'
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 foldl[D, L](
315
- iterable: Iterable[D], f: Callable[[L, D], L], initial: L, /
316
- ) -> L | Never:
317
- """Folds an iterable left with a function and initial value.
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
- - traditional FP type order given for function ``f``
320
- - does not catch any exceptions ``f`` may raise
321
- - like builtin ``sum`` for Python >=3.8 except
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
- - not restricted to ``__add__`` for the folding function
324
- - initial value required, does not default to ``0`` for initial value
325
- - handles non-numeric data just find
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 mb_fold_left[L, D](
336
- iterable: Iterable[D], f: Callable[[L, D], L], initial: L | NoValue = NoValue()
337
- ) -> MB[L]:
338
- """Folds an iterable left with optional initial value.
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 ``~L = ~D``
342
- - if iterable empty and no ``initial`` value given, return ``MB()``
343
- - never returns if iterable generates an infinite iterator
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 MB()
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 MB()
522
+ return MayBe()
360
523
 
361
- return MB(acc)
524
+ return MayBe(acc)
362
525
 
363
526
 
364
527
  def sc_reduce_left[D](
365
- iterable: Iterable[D],
366
- f: Callable[[D, D], D],
367
- /,
368
- start: Callable[[D], bool] = (lambda d: True),
369
- stop: Callable[[D], bool] = (lambda d: False),
370
- include_start: bool = True,
371
- include_stop: bool = True,
372
- ) -> tuple[MB[D], Iterator[D]]:
373
- """Short circuit version of a left reduce. Useful for infinite or iterables.
374
-
375
- - Behavior for default arguments will
376
-
377
- - left reduce finite iterable
378
- - start folding immediately
379
- - continue folding until end (of a possibly infinite iterable)
380
-
381
- - Callable ``start`` delays starting the left reduce
382
- - Callable ``stop`` prematurely stop the left reduce
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 = mb_fold_left(it_reduce, f)
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 = MB(f(mb_reduced.get(), last))
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 = MB(last)
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
- iterable: Iterable[D],
411
- f: Callable[[D, D], D],
412
- /,
413
- start: Callable[[D], bool] = (lambda d: False),
414
- stop: Callable[[D], bool] = (lambda d: False),
415
- include_start: bool = True,
416
- include_stop: bool = True,
417
- ) -> tuple[MB[D], Iterator[D]]:
418
- """Short circuit version of a right reduce. Useful for infinite or
419
- non-reversible iterables.
420
-
421
- - Behavior for default arguments will
422
-
423
- - right reduce finite iterable
424
- - start reducing at end (of a possibly infinite iterable)
425
- - continue reducing right until beginning
426
-
427
- - Callable ``start`` prematurely starts the right reduce
428
- - Callable ``stop`` prematurely stops the right reduce
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
- l1 = list(it_start)
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
- l1.append(begin)
636
+ list1.append(begin)
439
637
 
440
- l1.reverse()
441
- it_reduce, it_stop = take_while_split(l1, negate(stop))
638
+ list1.reverse()
639
+ it_reduce, it_stop = take_while_split(list1, negate(stop))
442
640
 
443
- mb_reduced = mb_fold_left(it_reduce, swap(f))
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 = MB(f(end, mb_reduced.get()))
649
+ mb_reduced = MayBe(f(end, mb_reduced.get()))
452
650
  else:
453
- mb_reduced = MB(end)
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: 3.0.0
4
- Summary: Module fp.iterables - Iterator related tools
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://grscheller.github.io/pythonic-fp-iterables/html/changelog.html
21
- Project-URL: Documentation, https://grscheller.github.io/pythonic-fp-iterables/html/
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 package of tools for iterables and iterators.
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
- - Assumptions
35
- - iterables are not necessarily iterators
36
- - at all times iterator protocol is assumed to be followed
37
- - all iterators are assumed to be iterable
38
- - for all iterators `my_iter` we assume `iter(my_iter) is my_iter`
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-iterables/html).
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,,