Skip to content

Commit

Permalink
scan added
Browse files Browse the repository at this point in the history
  • Loading branch information
WitoldFracek committed Nov 12, 2024
1 parent 4597576 commit 1dea7c7
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 10 deletions.
30 changes: 28 additions & 2 deletions src/qwlist/eager.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def fold(self, operation: Callable[[K, T], K], init: K) -> K:
operation: `function: (K, T) -> K`
Given the initial value `init` applies the
given combination operator on each element of the EagerQList,
treating the result as a first argument in the next step.
treating the result as the first argument in the next step.
init: initial value for the combination operator.
Returns: `K`
Expand Down Expand Up @@ -141,6 +141,32 @@ def fold_right(self, operation: Callable[[K, T], K], init: K) -> K:
acc = operation(acc, elem)
return acc

def scan(self, operation: Callable[[K, T], K], state: K) -> "EagerQList[K]":
"""
Given the combination operator creates a new `EagerQList[K]` object by processing
constituent parts of `self`, yielding intermediate steps and building up the final value.
Scan is similar to fold but returns all intermediate states instead of just the final result.
Args:
operation: `function: (K, T) -> K`. Given the initial `state` applies the given
combination operator on each element of `self`, yielding the result and
then treating it as the first argument in the next step.
state (K): initial value for the state.
Returns:
`Lazy[K]` - iterable with all intermediate steps of the `operation`.
Examples:
>>> EagerQList([1, 2, 3]).scan(lambda acc, x: acc + x, 0)
[1, 3, 6]
"""
def inner(s):
for elem in self:
s = operation(s, elem)
yield s
return EagerQList(inner(state))

def len(self):
return len(self)

Expand Down Expand Up @@ -525,4 +551,4 @@ def sum(self) -> Optional[SupportsAdd]:
return acc

if __name__ == '__main__':
print(EagerQList([(1, 2), (3,)]).sum())
print(EagerQList([1, 2, 3]).scan(lambda acc, x: acc + x, 0))
60 changes: 56 additions & 4 deletions src/qwlist/qwlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ def fold(self, operation: Callable[[K, T], K], init: K) -> K:
Args:
operation: `function: (K, T) -> K`. Given the initial value `init` applies the
given combination operator on each element yielded by the Lazy object,
treating the result as a first argument in the next step.
given combination operator on each element yielded by the `Lazy` object,
treating the result as the first argument in the next step.
init: initial value for the combination operator.
Returns: `K`
Expand All @@ -119,6 +119,32 @@ def fold(self, operation: Callable[[K, T], K], init: K) -> K:
acc = operation(acc, elem)
return acc

def scan(self, operation: Callable[[K, T], K], state: K) -> "Lazy[K]":
"""
Given the combination operator creates a new `Lazy[K]` object by processing
constituent parts of `self`, yielding intermediate steps and building up the final value.
Scan is similar to fold but returns all intermediate states instead of just the final result.
Args:
operation: `function: (K, T) -> K`. Given the initial `state` applies the given
combination operator on each element yielded by the `Lazy` object, yielding the result and
then treating it as the first argument in the next step.
state (K): initial value for the state.
Returns:
`Lazy[K]` - iterable with all intermediate steps of the `operation`.
Examples:
>>> Lazy([1, 2, 3]).scan(lambda acc, x: acc + x, 0).collect()
[1, 3, 6]
"""
def inner(s):
for elem in self.gen:
s = operation(s, elem)
yield s
return Lazy(inner(state))

def foreach(self, action: Callable[[T], None]):
"""
Applies the given function to each of yielded elements.
Expand Down Expand Up @@ -718,7 +744,7 @@ def fold(self, operation: Callable[[K, T], K], init: K) -> K:
operation: `function: (K, T) -> K`
Given the initial value `init` applies the
given combination operator on each element of the `QList`,
treating the result as a first argument in the next step.
treating the result as the first argument in the next step.
init: initial value for the combination operator.
Returns: `K`
Expand Down Expand Up @@ -755,6 +781,32 @@ def fold_right(self, operation: Callable[[K, T], K], init: K) -> K:
acc = operation(acc, elem)
return acc

def scan(self, operation: Callable[[K, T], K], state: K) -> "Lazy[K]":
"""
Given the combination operator creates a new `Lazy[K]` object by processing
constituent parts of `self`, yielding intermediate steps and building up the final value.
Scan is similar to fold but returns all intermediate states instead of just the final result.
Args:
operation: `function: (K, T) -> K`. Given the initial `state` applies the given
combination operator on each element of `self`, yielding the result and
then treating it as the first argument in the next step.
state (K): initial value for the state.
Returns:
`Lazy[K]` - iterable with all intermediate steps of the `operation`.
Examples:
>>> QList([1, 2, 3]).scan(lambda acc, x: acc + x, 0).collect()
[1, 3, 6]
"""
def inner(s):
for elem in self:
s = operation(s, elem)
yield s
return Lazy(inner(state))

def len(self) -> int:
"""
Returns the len of the `QList`
Expand Down Expand Up @@ -1228,5 +1280,5 @@ def naturals(start):
.all(lambda x: n % x != 0)
))
)
print(Lazy([[1, 2, 3], [2, 3]]).min())
print(QList([1, 2, 3]).scan(lambda acc, x: acc + x, 0).collect())

6 changes: 3 additions & 3 deletions src/todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ QList, Lazy, EagerQList
[x] [x] [x] tested

### scan
[ ] [ ] [ ] implemented \
[ ] [ ] [ ] documented \
[ ] [ ] [ ] tested
[x] [x] [x] implemented \
[x] [x] [x] documented \
[x] [x] [x] tested

### min
[x] [x] [x] implemented \
Expand Down
24 changes: 24 additions & 0 deletions tests/test_eager.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ def test_fold():
res = EagerQList(range(10)).fold(lambda acc, x: 0, 0)
assert 0 == res

assert 0 == EagerQList().fold(lambda acc, x: acc + x, 0)


def test_fold_right():
expected = 6
Expand Down Expand Up @@ -374,3 +376,25 @@ def test_max():
assert EagerQList(range(10)).max() == 9
assert EagerQList().max() is None
assert EagerQList(['a', 'aaa', 'aa']).max(key=len) == 'aaa'


def test_scan():
expected = EagerQList([1, 3, 6])
res = EagerQList([1, 2, 3]).scan(lambda acc, x: acc + x, 0)
assert res == expected

expected = EagerQList(['1', '12', '123'])
res = EagerQList(['1', '2', '3']).scan(lambda acc, x: acc + x, '')
assert res == expected

expected = EagerQList(['1', '21', '321'])
res = EagerQList(['1', '2', '3']).scan(lambda acc, x: x + acc, '')
assert res == expected

expected = EagerQList([0, 0, 0, 0, 0])
res = EagerQList(range(5)).scan(lambda acc, x: 0, 0)
assert res == expected

expected = EagerQList()
res = EagerQList().scan(lambda acc, x: x, 0)
assert res == expected
26 changes: 25 additions & 1 deletion tests/test_lazy.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ def test_fold():
res = Lazy(range(10)).fold(lambda acc, x: 0, 0)
assert 0 == res

assert 0 == Lazy([]).fold(lambda acc, x: acc + x, 0)


def test_zip():
expected = QList([(1, 0), (2, 1), (3, 2)])
Expand Down Expand Up @@ -442,4 +444,26 @@ def test_min():
def test_max():
assert Lazy(range(10)).max() == 9
assert Lazy([]).max() is None
assert Lazy(['a', 'aaa', 'aa']).max(key=len) == 'aaa'
assert Lazy(['a', 'aaa', 'aa']).max(key=len) == 'aaa'


def test_scan():
expected = QList([1, 3, 6])
res = Lazy([1, 2, 3]).scan(lambda acc, x: acc + x, 0).collect()
assert res == expected

expected = QList(['1', '12', '123'])
res = Lazy(['1', '2', '3']).scan(lambda acc, x: acc + x, '').collect()
assert res == expected

expected = QList(['1', '21', '321'])
res = Lazy(['1', '2', '3']).scan(lambda acc, x: x + acc, '').collect()
assert res == expected

expected = QList([0, 0, 0, 0, 0])
res = Lazy(range(5)).scan(lambda acc, x: 0, 0).collect()
assert res == expected

expected = QList()
res = Lazy([]).scan(lambda acc, x: x, 0).collect()
assert res == expected
24 changes: 24 additions & 0 deletions tests/test_qwlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ def test_fold():
res = QList(range(10)).fold(lambda acc, x: 0, 0)
assert 0 == res

assert 0 == QList().fold(lambda acc, x: acc + x, 0)


def test_fold_right():
expected = 6
Expand Down Expand Up @@ -483,3 +485,25 @@ def test_max():
assert QList([]).max() is None
assert QList(['a', 'aaa', 'aa']).max(key=len) == 'aaa'


def test_scan():
expected = QList([1, 3, 6])
res = QList([1, 2, 3]).scan(lambda acc, x: acc + x, 0).collect()
assert res == expected

expected = QList(['1', '12', '123'])
res = QList(['1', '2', '3']).scan(lambda acc, x: acc + x, '').collect()
assert res == expected

expected = QList(['1', '21', '321'])
res = QList(['1', '2', '3']).scan(lambda acc, x: x + acc, '').collect()
assert res == expected

expected = QList([0, 0, 0, 0, 0])
res = QList(range(5)).scan(lambda acc, x: 0, 0).collect()
assert res == expected

expected = QList()
res = QList([]).scan(lambda acc, x: x, 0).collect()
assert res == expected

0 comments on commit 1dea7c7

Please sign in to comment.