-
Notifications
You must be signed in to change notification settings - Fork 0
kotlin-operators #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
776b2e9
3c04d0e
76798ff
00f06e0
a0cfe3d
c270934
7edd637
b8d8d2c
d2767a6
ab10bcc
a58d0c2
c5d921d
0b1a218
5a203fd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,3 @@ | ||
| .DS_Store | ||
| .idea/ | ||
| __pycache__/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,7 @@ | |
| W = TypeVar('W') | ||
| X = TypeVar('X') | ||
|
|
||
|
|
||
| def add(n: T) -> NumberToNumber: | ||
| return lambda x: x + n | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,11 @@ | ||
| from inspect import signature | ||
| from functools import lru_cache, reduce | ||
| from itertools import islice, chain | ||
| from functools import reduce | ||
| from inspect import signature | ||
| from itertools import islice, chain, count | ||
| from types import BuiltinFunctionType | ||
| from typing import Iterable, Iterator, TypeVar, Callable, Tuple, Optional, Generic, List, Any, Union | ||
| from typing import Iterable, Iterator, TypeVar, Callable, Tuple, Optional, Generic, List, Union | ||
|
|
||
| from python_streams.partials import compose | ||
|
|
||
| T = TypeVar('T') | ||
| V = TypeVar('V') | ||
|
|
@@ -19,13 +22,118 @@ def expanded_func(item: ...) -> V: | |
| else func) | ||
|
|
||
|
|
||
| def Not(value: bool): | ||
| return not value | ||
|
|
||
|
|
||
| class Stream(Generic[T], Iterable): | ||
| def __init__(self, items: Iterable[T] = ()): | ||
| def __init__(self, items: Iterable[T] = (), with_cache=True): | ||
| self.items = iter(items) | ||
| self.with_cache = with_cache | ||
| self.cache = None | ||
| self.is_consumed = False | ||
|
|
||
| def __iter__(self) -> Iterator[T]: | ||
| yield from self.items | ||
|
|
||
| def __len__(self) -> int: | ||
| return len(self.to_list()) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We probably don't want to do a to_list unless the user wants to cache |
||
|
|
||
| # start kotlin functions | ||
|
|
||
| def __eq__(self, other): | ||
| for self_item, other_item in zip(self, other): | ||
| if self_item != other_item: | ||
| return False | ||
| return True | ||
|
|
||
| def __contains__(self, item_or_iterable: Union[T, Iterable[T]]) -> bool: | ||
| return (self.contains_all(item_or_iterable) | ||
| if isinstance(item_or_iterable, Iterable) | ||
| else self.contains(item_or_iterable)) | ||
|
|
||
| def contains(self, item: T) -> bool: | ||
| return item in self.items | ||
|
|
||
| def contains_all(self, iterator: Iterable[T]) -> bool: | ||
| return set(iterator).issubset(set(self)) | ||
|
|
||
| def __getitem__(self, index: int) -> T: | ||
| return self.get(index) | ||
|
|
||
| def get(self, index: int) -> T: | ||
| return next(islice(self.items, index, index + 1)) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TODO: Benchmark vs for loop |
||
|
|
||
| def index_of(self, item: T) -> int: | ||
| for index, self_item in zip(self.items, count()): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also aren't items and count() in the wrong order in the zip? |
||
| if self_item == item: | ||
| return index | ||
| return -1 | ||
|
|
||
| def is_empty(self) -> bool: | ||
| try: | ||
| self.items.__next__() | ||
| return False | ||
| except StopIteration: | ||
| return True | ||
|
|
||
| def last_index_of(self, item: T) -> int: | ||
| last_index = -1 | ||
| for index, self_item in zip(count(), self.items): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. zip can be changed to enumerate |
||
| if self_item == item: | ||
| last_index = index | ||
| return last_index | ||
|
|
||
| def sub_stream(self, from_index_inclusive: int, to_index_exclusive: int) -> 'Stream[T]': | ||
| return Stream(islice(self, from_index_inclusive, to_index_exclusive)) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice, could we bind |
||
|
|
||
| def indices(self) -> 'Stream[int]': | ||
| return Stream(range(len(self))) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What example use would this function have? |
||
|
|
||
| def last_index(self) -> int: | ||
| return len(self) - 1 | ||
|
|
||
| def all(self, condition: Filter) -> bool: | ||
| return list(filter(compose(expand(condition), Not), self.items)) == [] | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mola la implementación pero creo que no es muy performant, seguramente un for y salir cuando uno sea False es lo mejor. De todas formas python tiene un all() que igual puede ser rápido. Seguramente |
||
|
|
||
| def any(self, condition: Filter) -> bool: | ||
| return list(filter(expand(condition), self.items)) != [] | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lo mismo que all |
||
|
|
||
| def count(self) -> int: | ||
| return len(self) | ||
|
|
||
| # TODO: Fix non laziness | ||
| def distinct(self) -> 'Stream[T]': | ||
| unique_items = [] | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Podemos usar un set comprehension (yo también me acabo de enterar de que existen XD) |
||
| for item in self.to_list(): | ||
| if item not in unique_items: | ||
| unique_items.append(item) | ||
| return Stream(unique_items) | ||
|
|
||
| # TODO: Fix non laziness | ||
| def distinct_by(self, selector: Transform) -> 'Stream[T]': | ||
| unique_items = [] | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lazy implementation (untested) |
||
| unique_keys = [] | ||
| for item in self.to_list(): | ||
| key = expand(selector)(item) | ||
| if key not in unique_keys: | ||
| unique_keys.append(key) | ||
| unique_items.append(item) | ||
| return Stream(unique_items) | ||
|
|
||
| def drop(self, n: int) -> 'Stream[T]': | ||
| for i in range(n): | ||
| it = next(self.items, None) | ||
| if it is None: | ||
| return Stream(()) | ||
| return Stream(self.items) | ||
|
|
||
| def drop_last(self) -> 'Stream[T]': | ||
| items = self.to_list()[:-1] | ||
| return Stream(items) | ||
|
|
||
| # end kotlin functions | ||
|
|
||
| def map(self, func: Transform) -> 'Stream[V]': | ||
| return Stream(map(expand(func), self.items)) | ||
|
|
||
|
|
@@ -51,13 +159,6 @@ def max(self, key: Optional[Transform] = None) -> T: | |
| def take(self, n: int) -> 'Stream[T]': | ||
| return Stream(islice(self, n)) | ||
|
|
||
| def drop(self, n: int) -> 'Stream[T]': | ||
| for i in range(n): | ||
| it = next(self.items, None) | ||
| if it is None: | ||
| return Stream(()) | ||
| return Stream(self.items) | ||
|
|
||
| def first(self) -> T: | ||
| return next(self.items) | ||
|
|
||
|
|
@@ -73,6 +174,17 @@ def append(self, item: T) -> 'Stream[T]': | |
| def extend(self, item: T) -> 'Stream[T]': | ||
| return self.chain(Stream((item,))) | ||
|
|
||
| @lru_cache(1) | ||
| class AlreadyConsumed(Exception): | ||
| pass | ||
|
|
||
| def to_list(self) -> List[T]: | ||
| return list(self.items) | ||
| if self.with_cache: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we make self.items be a
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The nice thing about this is that if we do that then every method in this class automatically uses the cache if it's available instead of throwing an exception, without changing any code. |
||
| if not self.cache: | ||
| self.cache = list(self.items) | ||
| return self.cache | ||
| else: | ||
| if self.is_consumed: | ||
| raise self.AlreadyConsumed | ||
| else: | ||
| self.is_consumed = True | ||
| return list(self.items) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this could be moved to commodities.py (which we can rename sugar.py)