Skip to content

Commit cdbbed5

Browse files
authored
Add mapi, append and delay for Seq and FrozenList (#18)
* Add mapi, append and delay for Seq and FrozenList * Add test for seq delay
1 parent 9018f55 commit cdbbed5

File tree

7 files changed

+232
-37
lines changed

7 files changed

+232
-37
lines changed

.pre-commit-config.yaml

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,30 @@
11
exclude: "_version.py|versioneer.py"
22
repos:
3-
- hooks:
4-
- args:
5-
- --remove-all-unused-imports
6-
- --in-place
7-
id: autoflake
8-
repo: https://github.com/humitos/mirrors-autoflake
9-
rev: v1.1
10-
- hooks:
11-
- id: isort
12-
repo: https://github.com/timothycrosley/isort
13-
rev: 5.6.4
14-
- hooks:
15-
- id: black
16-
repo: https://github.com/psf/black
17-
rev: 20.8b1
18-
- hooks:
19-
- id: flake8
20-
repo: https://gitlab.com/pycqa/flake8
21-
rev: 3.8.4
22-
- hooks:
23-
- id: pyright
24-
name: pyright
25-
entry: pyright
26-
language: node
27-
pass_filenames: false
28-
types: [python]
29-
additional_dependencies: ['[email protected]']
30-
repo: local
31-
3+
- hooks:
4+
- args:
5+
- --remove-all-unused-imports
6+
- --in-place
7+
id: autoflake
8+
repo: https://github.com/humitos/mirrors-autoflake
9+
rev: v1.1
10+
- hooks:
11+
- id: isort
12+
repo: https://github.com/timothycrosley/isort
13+
rev: 5.6.4
14+
- hooks:
15+
- id: black
16+
repo: https://github.com/psf/black
17+
rev: 20.8b1
18+
- hooks:
19+
- id: flake8
20+
repo: https://gitlab.com/pycqa/flake8
21+
rev: 3.8.4
22+
- hooks:
23+
- id: pyright
24+
name: pyright
25+
entry: pyright
26+
language: node
27+
pass_filenames: false
28+
types: [python]
29+
additional_dependencies: ["[email protected]"]
30+
repo: local

expression/collections/frozenlist.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import builtins
2323
import functools
24+
import itertools
2425
from typing import (
2526
Any,
2627
Callable,
@@ -268,6 +269,23 @@ def map(self, mapping: Callable[[TSource], TResult]) -> FrozenList[TResult]:
268269
"""
269270
return FrozenList((*builtins.map(mapping, self),))
270271

272+
def mapi(self, mapping: Callable[[int, TSource], TResult]) -> FrozenList[TResult]:
273+
"""Map list with index.
274+
275+
Builds a new collection whose elements are the results of
276+
applying the given function to each of the elements of the
277+
collection. The integer index passed to the function indicates
278+
the index (from 0) of element being transformed.
279+
280+
Args:
281+
mapping: The function to transform elements and their
282+
indices.
283+
284+
Returns:
285+
The list of transformed elements.
286+
"""
287+
return FrozenList((*itertools.starmap(mapping, enumerate(self)),))
288+
271289
@staticmethod
272290
def of(*args: TSource) -> FrozenList[TSource]:
273291
"""Create list from a number of arguments."""
@@ -625,6 +643,28 @@ def _map(source: FrozenList[TSource]) -> FrozenList[TResult]:
625643
return _map
626644

627645

646+
def mapi(mapper: Callable[[int, TSource], TResult]) -> Projection[TSource, TResult]:
647+
"""Map list with index.
648+
649+
Builds a new collection whose elements are the results of
650+
applying the given function to each of the elements of the
651+
collection. The integer index passed to the function indicates
652+
the index (from 0) of element being transformed.
653+
654+
Args:
655+
mapping: The function to transform elements and their
656+
indices.
657+
658+
Returns:
659+
The list of transformed elements.
660+
"""
661+
662+
def _mapi(source: FrozenList[TSource]) -> FrozenList[TResult]:
663+
return source.mapi(mapper)
664+
665+
return _mapi
666+
667+
628668
def of(*args: TSource) -> FrozenList[TSource]:
629669
"""Create list from a number of arguments."""
630670
return FrozenList((*args,))
@@ -812,6 +852,7 @@ def _zip(source: FrozenList[TSource]) -> FrozenList[Tuple[TSource, TResult]]:
812852
"item",
813853
"is_empty",
814854
"map",
855+
"mapi",
815856
"of_seq",
816857
"of_option",
817858
"singleton",

expression/collections/seq.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ def of(cls, *args: TSource) -> Seq[TSource]:
5656
def of_iterable(cls, source: Iterable[TSource]) -> Seq[TSource]:
5757
return cls(source)
5858

59+
def append(self, *others: Iterable[TSource]) -> Seq[TSource]:
60+
"""Wraps the two given enumerations as a single concatenated
61+
enumeration."""
62+
return Seq(concat(self, *others))
63+
5964
def filter(self, predicate: Callable[[TSource], bool]) -> Seq[TSource]:
6065
return Seq(filter(predicate)(self))
6166

@@ -81,6 +86,20 @@ def collect(self, mapping: Callable[[TSource], "Seq[TResult]"]) -> Seq[TResult]:
8186
xs = pipe(self, collect(mapping))
8287
return Seq(xs)
8388

89+
@staticmethod
90+
def delay(generator: Callable[[], Iterable[TSource]]) -> Iterable[TSource]:
91+
"""Returns a sequence that is built from the given delayed specification of a
92+
sequence.
93+
94+
The input function is evaluated each time an IEnumerator for the sequence
95+
is requested.
96+
97+
Args:
98+
generator: The generating function for the sequence.
99+
"""
100+
101+
return delay(generator)
102+
84103
@staticmethod
85104
def empty() -> Seq[TSource]:
86105
"""Returns empty sequence."""
@@ -123,6 +142,23 @@ def map(self, mapper: Callable[[TSource], TResult]) -> Seq[TResult]:
123142

124143
return Seq(map(mapper)(self))
125144

145+
def mapi(self, mapping: Callable[[int, TSource], TResult]) -> Seq[TResult]:
146+
"""Map list with index.
147+
148+
Builds a new collection whose elements are the results of
149+
applying the given function to each of the elements of the
150+
collection. The integer index passed to the function indicates
151+
the index (from 0) of element being transformed.
152+
153+
Args:
154+
mapping: The function to transform elements and their
155+
indices.
156+
157+
Returns:
158+
The list of transformed elements.
159+
"""
160+
return Seq(mapi(mapping)(self))
161+
126162
@overload
127163
def match(self) -> Case[Iterable[TSource]]:
128164
...
@@ -242,6 +278,16 @@ def __call__(self, __source: Iterable[TSource]) -> Iterable[TResult]:
242278
raise NotImplementedError
243279

244280

281+
def append(*others: Iterable[TSource]) -> Projection[TSource, TSource]:
282+
"""Wraps the given enumerations as a single concatenated
283+
enumeration."""
284+
285+
def _(source: Iterable[TSource]) -> Iterable[TSource]:
286+
return concat(source, *others)
287+
288+
return _
289+
290+
245291
def choose(chooser: Callable[[TSource], Option[TResult]]) -> Projection[TSource, TResult]:
246292
"""Choose items from the sequence.
247293
@@ -290,6 +336,31 @@ def concat(*iterables: Iterable[TSource]) -> Iterable[TSource]:
290336
yield element
291337

292338

339+
def delay(generator: Callable[[], Iterable[TSource]]) -> Iterable[TSource]:
340+
"""Returns a sequence that is built from the given delayed
341+
specification of a sequence.
342+
343+
The input function is evaluated each time an Iterator for the
344+
sequence is requested.
345+
346+
Args:
347+
generator: The generating function for the sequence.
348+
"""
349+
350+
class Delayed(Iterable[TResult]):
351+
"""An infinite iterable where each iterator starts counting at
352+
0."""
353+
354+
def __init__(self, gen: Callable[[], Iterable[TResult]]) -> None:
355+
self.gen = gen
356+
357+
def __iter__(self) -> Iterator[TResult]:
358+
xs = self.gen()
359+
return builtins.iter(xs)
360+
361+
return Delayed(generator)
362+
363+
293364
empty: Seq[Any] = Seq()
294365
"""The empty sequence."""
295366

@@ -481,6 +552,28 @@ def _map(source: Iterable[TSource]) -> Iterable[TResult]:
481552
return _map
482553

483554

555+
def mapi(mapping: Callable[[int, TSource], TResult]) -> Projection[TSource, TResult]:
556+
"""Map list with index.
557+
558+
Builds a new collection whose elements are the results of
559+
applying the given function to each of the elements of the
560+
collection. The integer index passed to the function indicates
561+
the index (from 0) of element being transformed.
562+
563+
Args:
564+
mapping: The function to transform elements and their
565+
indices.
566+
567+
Returns:
568+
The list of transformed elements.
569+
"""
570+
571+
def _mapi(source: Iterable[TSource]) -> Iterable[TResult]:
572+
return (*itertools.starmap(mapping, builtins.enumerate(source)),)
573+
574+
return _mapi
575+
576+
484577
def max(source: Iterable[TSupportsLessThan]) -> TSupportsLessThan:
485578
"""Returns the greatest of all elements of the sequence,
486579
compared via `max()`."""
@@ -680,16 +773,19 @@ def _zip(source2: Iterable[TResult]) -> Iterable[Tuple[TSource, TResult]]:
680773

681774
__all__ = [
682775
"Seq",
776+
"append",
683777
"choose",
684778
"concat",
685779
"collect",
780+
"delay",
686781
"empty",
687782
"filter",
688783
"fold",
689784
"fold_back",
690785
"head",
691786
"iter",
692787
"map",
788+
"mapi",
693789
"max",
694790
"min",
695791
"min_by",

expression/core/aiotools.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,15 @@ def cb():
6868
def start_immediate(computation: Awaitable[Any], token: Optional[CancellationToken] = None) -> None:
6969
task = asyncio.create_task(computation)
7070

71-
def cb():
71+
def cb() -> None:
7272
task.cancel()
7373

7474
if token:
7575
token.register(cb)
7676
return None
7777

7878

79-
def run_synchronous(computation: Awaitable[TSource]) -> TSource:
79+
def run_synchronously(computation: Awaitable[TSource]) -> TSource:
8080
"""Runs the asynchronous computation and await its result."""
8181
return asyncio.run(computation)
8282

@@ -114,4 +114,5 @@ async def from_result(result: TSource) -> TSource:
114114
"sleep",
115115
"start",
116116
"start_immediate",
117+
"run_synchronously",
117118
]

expression/core/misc.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def starid(*value: Any) -> Tuple[Any, ...]:
2020
return value
2121

2222

23-
def flip(fn: Callable[[A, B], Any]) -> Callable[[B, A], Any]:
23+
def flip(fn: Callable[[A, B], TResult]) -> Callable[[B, A], TResult]:
2424
"""Flips the arguments for a function taking two arguments.
2525
2626
Example:
@@ -33,14 +33,14 @@ def _flip(b: B, a: A) -> Any:
3333
return _flip
3434

3535

36-
def snd(value: Tuple[Any, B]) -> B:
36+
def snd(value: Tuple[Any, TSource]) -> TSource:
3737
"""Return second argument of the tuple."""
3838

3939
_, b = value
4040
return b
4141

4242

43-
def fst(value: Tuple[A, Any]) -> A:
43+
def fst(value: Tuple[TSource, Any]) -> TSource:
4444
"""Return first argument of the tuple."""
4545

4646
a, _ = value

tests/test_frozenlist.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,18 @@ def mapper(x: int):
100100
assert [y for y in zs] == [mapper(x) for x in xs]
101101

102102

103+
@given(st.lists(st.integers()))
104+
def test_list_pipe_mapi(xs: List[int]):
105+
def mapper(i: int, x: int):
106+
return x + i
107+
108+
ys = frozenlist.of_seq(xs)
109+
zs = ys.pipe(frozenlist.mapi(mapper))
110+
111+
assert isinstance(zs, FrozenList)
112+
assert [z for z in zs] == [x + i for i, x in enumerate(xs)]
113+
114+
103115
@given(st.lists(st.integers()))
104116
def test_list_len(xs: List[int]):
105117
ys = frozenlist.of_seq(xs)

0 commit comments

Comments
 (0)