Skip to content

Commit

Permalink
window added
Browse files Browse the repository at this point in the history
  • Loading branch information
WitoldFracek committed Nov 13, 2024
1 parent 1dea7c7 commit a957d4e
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 6 deletions.
30 changes: 29 additions & 1 deletion src/qwlist/eager.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from collections import deque
from typing import TypeVar, Iterable, Callable, overload, Iterator, Optional, Type

from src.qwlist import QList
Expand Down Expand Up @@ -550,5 +551,32 @@ def sum(self) -> Optional[SupportsAdd]:
acc = acc + elem
return acc

def window(self, window_size: int) -> "EagerQList[EagerQList[T]]":
"""
Creates a new `EagerQList` of sliding windows of size `window_size`. If `window_size`
is greater than the total length of `self` an empty iterator is returned.
Args:
window_size (int): the size of the sliding window. Must be greater than 0.
Returns:
`EagerQList[EagerQList[T]]` - iterable of all sliding windows.
"""
assert window_size > 0, f'window size must be greater than 0 but got {window_size}.'
def inner(n: int):
if self.len() < n:
return
if self.len() == n:
yield self
return
window = deque(maxlen=n)
for elem in self[:n]:
window.append(elem)
yield EagerQList(window)
for elem in self[n:]:
window.append(elem)
yield EagerQList(window)
return EagerQList(inner(n=window_size))

if __name__ == '__main__':
print(EagerQList([1, 2, 3]).scan(lambda acc, x: acc + x, 0))
print(EagerQList([1, 2, 3]).window(2))
57 changes: 56 additions & 1 deletion src/qwlist/qwlist.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import TypeVar, Generic, Iterable, Callable, overload, Optional, Iterator, Type
from collections import deque

T = TypeVar('T')
K = TypeVar('K')
Expand Down Expand Up @@ -619,6 +620,32 @@ def inner():
yield elem
return Lazy(inner())

def window(self, window_size: int) -> "Lazy[QList[T]]":
"""
Creates a new `Lazy` of sliding windows of size `window_size`. If `window_size`
is greater than the total length of `self` an empty iterator is returned.
Args:
window_size (int): the size of the sliding window. Must be greater than 0.
Returns:
`Lazy[QList[T]]` - iterable of all sliding windows.
"""
assert window_size > 0, f'window size must be greater than 0 but got {window_size}.'
def inner(n: int):
window = deque(maxlen=n)
it = self.iter()
try:
for _ in range(n):
window.append(next(it))
except StopIteration:
return
yield QList(window)
for elem in it:
window.append(elem)
yield QList(window)
return Lazy(inner(n=window_size))


# ----------------- QList ----------------------------------------------

Expand Down Expand Up @@ -1264,6 +1291,33 @@ def sum(self) -> Optional[SupportsAdd]:
acc = acc + elem
return acc

def window(self, window_size: int) -> "Lazy[QList[T]]":
"""
Creates a new `Lazy` of sliding windows of size `window_size`. If `window_size`
is greater than the total length of `self` an empty iterator is returned.
Args:
window_size (int): the size of the sliding window. Must be greater than 0.
Returns:
`Lazy[QList[T]]` - iterable of all sliding windows.
"""
assert window_size > 0, f'window size must be greater than 0 but got {window_size}.'
def inner(n: int):
if self.len() < n:
return
if self.len() == n:
yield self
return
window = deque(maxlen=n)
for elem in self[:n]:
window.append(elem)
yield QList(window)
for elem in self[n:]:
window.append(elem)
yield QList(window)
return Lazy(inner(n=window_size))


if __name__ == '__main__':
def naturals(start):
Expand All @@ -1280,5 +1334,6 @@ def naturals(start):
.all(lambda x: n % x != 0)
))
)
print(QList([1, 2, 3]).scan(lambda acc, x: acc + x, 0).collect())
acc_sum = QList(range(10)).window(3).collect()
print(acc_sum)

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

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

### iter
[x] [x] [x] implemented \
Expand Down
37 changes: 37 additions & 0 deletions tests/test_eager.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,3 +398,40 @@ def test_scan():
expected = EagerQList()
res = EagerQList().scan(lambda acc, x: x, 0)
assert res == expected


def test_window():
expected = EagerQList()
res = EagerQList().window(2)
assert res == expected

expected = EagerQList()
res = EagerQList(range(10)).window(100)
assert res == expected

expected = EagerQList([[0, 1], [1, 2], [2, 3]])
res = EagerQList(range(4)).window(2)
assert res == expected

expected = EagerQList([[0, 1, 2]])
res = EagerQList(range(3)).window(3)
assert res == expected

try:
EagerQList(range(10)).window(-4)
except Exception:
assert True
else:
assert False

expected = EagerQList([[[0, 1, 2], [1, 2, 3]]])
res = EagerQList(range(4)).window(3).window(2)
assert res == expected

expected = EagerQList([[i] for i in range(4)])
res = EagerQList(range(4)).window(1)
assert res == expected

expected = EagerQList()
res = EagerQList(range(5)).window(6)
assert res == expected
37 changes: 37 additions & 0 deletions tests/test_lazy.py
Original file line number Diff line number Diff line change
Expand Up @@ -466,4 +466,41 @@ def test_scan():

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


def test_window():
expected = QList()
res = Lazy([]).window(2).collect()
assert res == expected

expected = QList()
res = Lazy(range(10)).window(100).collect()
assert res == expected

expected = QList([[0, 1], [1, 2], [2, 3]])
res = Lazy(range(4)).window(2).collect()
assert res == expected

expected = QList([[0, 1, 2]])
res = Lazy(range(3)).window(3).collect()
assert res == expected

try:
Lazy(range(10)).window(-4).collect()
except Exception:
assert True
else:
assert False

expected = QList([[[0, 1, 2], [1, 2, 3]]])
res = Lazy(range(4)).window(3).window(2).collect()
assert res == expected

expected = QList([[i] for i in range(4)])
res = Lazy(range(4)).window(1).collect()
assert res == expected

expected = QList()
res = Lazy(range(5)).window(6).collect()
assert res == expected
41 changes: 40 additions & 1 deletion tests/test_qwlist.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import sys; sys.path.append('../src')
import sys;
from gc import collect

sys.path.append('../src')
from src.qwlist.qwlist import QList, Lazy
from src.qwlist.eager import EagerQList
import pytest
Expand Down Expand Up @@ -507,3 +510,39 @@ def test_scan():
res = QList([]).scan(lambda acc, x: x, 0).collect()
assert res == expected


def test_window():
expected = QList()
res = QList().window(2).collect()
assert res == expected

expected = QList()
res = QList(range(10)).window(100).collect()
assert res == expected

expected = QList([[0, 1], [1, 2], [2, 3]])
res = QList(range(4)).window(2).collect()
assert res == expected

expected = QList([[0, 1, 2]])
res = QList(range(3)).window(3).collect()
assert res == expected

try:
QList(range(10)).window(-4).collect()
except Exception:
assert True
else:
assert False

expected = QList([[[0, 1, 2], [1, 2, 3]]])
res = QList(range(4)).window(3).window(2).collect()
assert res == expected

expected = QList([[i] for i in range(4)])
res = QList(range(4)).window(1).collect()
assert res == expected

expected = QList()
res = QList(range(5)).window(6).collect()
assert res == expected

0 comments on commit a957d4e

Please sign in to comment.