Skip to content

Commit

Permalink
#240 more type hinting improvements after mypy recommendations
Browse files Browse the repository at this point in the history
  • Loading branch information
wojtask committed Oct 6, 2024
1 parent edc8c99 commit c057e8f
Show file tree
Hide file tree
Showing 14 changed files with 60 additions and 49 deletions.
2 changes: 1 addition & 1 deletion src/book/chapter2/problem3.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def horner(A: Array[float], n: int, x: float) -> float:
Returns:
The value of the polynomial for x.
"""
p = 0
p = 0.0
for i in range_of(n, downto=0):
p = A[i] + x * p
return p
2 changes: 1 addition & 1 deletion src/book/chapter2/section3.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def insertion_sort_with_binary_search(A: Array[CT], n: int) -> None:
A[k] = key


def __binary_search_position(A: Array[CT], x: int, p: int, r: int) -> int:
def __binary_search_position(A: Array[CT], x: CT, p: int, r: int) -> int:
if p > r:
return p
q = (p + r) // 2
Expand Down
6 changes: 3 additions & 3 deletions src/book/chapter5/problem2.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from typing import Optional
from __future__ import annotations

from book.chapter5.section3 import randomly_permute
from book.data_structures import Array
from book.data_structures import T
from util import range_of


def deterministic_search(A: Array[T], n: int, x: T) -> Optional[int]:
def deterministic_search(A: Array[T], n: int, x: T) -> int | None:
"""Searches for a value in an array, examining its elements sequentially.
Implements:
Expand All @@ -26,7 +26,7 @@ def deterministic_search(A: Array[T], n: int, x: T) -> Optional[int]:
return None


def scramble_search(A: Array[T], n: int, x: T) -> Optional[int]:
def scramble_search(A: Array[T], n: int, x: T) -> int | None:
"""Searches for a value in an array by first permuting it, then examining its elements sequentially.
Implements:
Expand Down
6 changes: 3 additions & 3 deletions src/book/chapter5/section3.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def randomized_hire_assistant(rank: Array[int], n: int) -> None:
hire_assistant(rank, n)


def randomly_permute(A: Array[int], n: int) -> None:
def randomly_permute(A: Array[T], n: int) -> None:
"""Permutes an array in place, producing a uniform random permutation.
Implements:
Expand All @@ -35,7 +35,7 @@ def randomly_permute(A: Array[int], n: int) -> None:
A[i], A[j] = A[j], A[i]


def permute_without_identity(A: Array[int], n: int) -> None:
def permute_without_identity(A: Array[T], n: int) -> None:
"""A deliberately faulty algorithm for producing any permutation of an array without the identity permutation.
Fails to produce any permutation in which an element on a given position occupied the same position in the original
array.
Expand Down Expand Up @@ -103,7 +103,7 @@ def random_sample(m: int, n: int) -> set[int]:
m: the cardinality of the sample
n: the cardinality of the set, 0 <= m <= n
"""
S = set()
S = set[int]()
for k in range_of(n - m + 1, to=n):
i = random(1, k)
if i in S:
Expand Down
30 changes: 19 additions & 11 deletions src/book/data_structures.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,37 @@
from __future__ import annotations

from builtins import len
from collections.abc import Callable
from typing import Any
from typing import Generic
from typing import Literal
from typing import TypeVar
from typing import Union

from typing_extensions import Protocol
from typing_extensions import Self


T = TypeVar('T')
CT = TypeVar('CT', bound='Comparable')


class Comparable(Protocol):
def __lt__(self, other: Any) -> bool:
pass

def __le__(self, other: Any) -> bool:
pass

def __gt__(self, other: Any) -> bool:
pass

def __ge__(self, other: Any) -> bool:
pass


class Indexable(Protocol[T]):
__getitem__: Callable[[int], T]
__setitem__: Callable[[int, T], None]


CT = TypeVar('CT', bound=Comparable)
Bit = Literal[0, 1]


Expand All @@ -34,7 +42,7 @@ class Array(Generic[T]):
def __init__(self, start: int, end: int) -> None:
assert 0 <= start <= end
self.__start = start
self.__elements = [None] * (end - start + 1)
self.__elements = [None] * (end - start + 1) # type: ignore

def __getitem__(self, index: int) -> T:
assert self.__start <= index < self.__start + len(self.__elements)
Expand All @@ -61,10 +69,10 @@ class Matrix:
__end_row: int
__start_col: int
__end_col: int
__elements: list[list[Union[int, float]]]
__elements: list[list[int | float]]

def __init__(self, end_row: int, end_col: int, start_row: int = 1, start_col: int = 1,
elements: list[list[Union[int, float]]] = None) -> None:
elements: list[list[int | float]] | None = None) -> None:
if elements is None:
assert start_row == 1
assert start_col == 1
Expand All @@ -74,28 +82,28 @@ def __init__(self, end_row: int, end_col: int, start_row: int = 1, start_col: in
self.__start_row, self.__end_row = start_row, end_row
self.__start_col, self.__end_col = start_col, end_col

def submatrix(self, rows: tuple[int, int], cols: tuple[int, int]) -> Self:
def submatrix(self, rows: tuple[int, int], cols: tuple[int, int]) -> "Matrix":
assert 1 <= rows[0] <= rows[1] <= len(self.__elements)
assert 1 <= cols[0] <= cols[1] <= len(self.__elements[0])
rows_shifted = rows[0] + self.__start_row - 1, rows[1] + self.__start_row - 1
cols_shifted = cols[0] + self.__start_col - 1, cols[1] + self.__start_col - 1
return Matrix(start_row=rows_shifted[0], end_row=rows_shifted[1], start_col=cols_shifted[0],
end_col=cols_shifted[1], elements=self.__elements)

def even_rows_submatrix(self) -> Self:
def even_rows_submatrix(self) -> "Matrix":
even_rows = [row for i, row in enumerate(self.__elements, start=1) if i % 2 == 0]
submatrix_end_row = self.__start_row + len(even_rows) - 1
return Matrix(start_row=self.__start_row, end_row=submatrix_end_row, start_col=self.__start_col,
end_col=self.__end_col, elements=even_rows)

def __getitem__(self, indices: tuple[int, int]) -> Union[int, float]:
def __getitem__(self, indices: tuple[int, int]) -> int | float:
row = indices[0]
col = indices[1]
assert 1 <= row <= self.__end_row - self.__start_row + 1
assert 1 <= col <= self.__end_col - self.__start_col + 1
return self.__elements[self.__start_row - 1 + row - 1][self.__start_col - 1 + col - 1]

def __setitem__(self, indices: tuple[int, int], value: Union[int, float]) -> None:
def __setitem__(self, indices: tuple[int, int], value: int | float) -> None:
row = indices[0]
col = indices[1]
assert 1 <= row <= self.__end_row - self.__start_row + 1
Expand Down
2 changes: 1 addition & 1 deletion src/solutions/chapter2/problem3.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def polynomial_evaluate(A: Array[float], n: int, x: float) -> float:
Returns:
The value of the polynomial for x.
"""
p = 0
p = 0.0
for i in range_of(0, to=n):
s = A[i]
for j in range_of(1, to=i):
Expand Down
5 changes: 2 additions & 3 deletions src/solutions/chapter2/section1/exercise4.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from typing import Optional
from __future__ import annotations

from book.data_structures import Array
from book.data_structures import T
from util import range_of


def linear_search(A: Array[T], n: int, x: int) -> Optional[int]:
def linear_search(A: Array[int], n: int, x: int) -> int | None:
"""Searches for a number in a sequence of numbers using linear search.
Implements:
Expand Down
4 changes: 2 additions & 2 deletions src/solutions/chapter2/section1/exercise5.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def add_binary_integers(A: Array[Bit], B: Array[Bit], n: int) -> Array[Bit]:
C = Array[Bit](0, n)
carry = 0
for i in range_of(0, to=n - 1):
C[i] = (A[i] + B[i] + carry) % 2
C[i] = (A[i] + B[i] + carry) % 2 # type: ignore
carry = (A[i] + B[i] + carry) // 2
C[n] = carry
C[n] = carry # type: ignore
return C
5 changes: 2 additions & 3 deletions src/solutions/chapter2/section3/exercise6.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from typing import Optional
from __future__ import annotations

from book.data_structures import Array
from book.data_structures import CT


def binary_search(A: Array[CT], p: int, r: int, x: int) -> Optional[int]:
def binary_search(A: Array[int], p: int, r: int, x: int) -> int | None:
"""Searches for a number in a sorted sequence of numbers using binary search.
Implements:
Expand Down
17 changes: 9 additions & 8 deletions src/solutions/chapter4/problem7.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
from typing import Optional
from typing import Union
from __future__ import annotations

from book.data_structures import Array
from book.data_structures import Matrix
from util import ceil_div
from util import range_of


def monge_leftmost_minimums(A: Matrix, m: int, n: int) -> Array[Union[int, float]]:
def monge_leftmost_minimums(A: Matrix, m: int, n: int) -> Array[int | float]:
"""Finds the leftmost minimum in each row of a Monge array.
Args:
Expand All @@ -25,24 +24,26 @@ def monge_leftmost_minimums(A: Matrix, m: int, n: int) -> Array[Union[int, float
return minimums


def __monge_leftmost_minimums_indices(A: Matrix, m: int, n: int) -> Optional[Array[Union[int, float]]]:
def __monge_leftmost_minimums_indices(A: Matrix, m: int, n: int) -> Array[int] | None:
if m == 0:
return None
A_ = A.even_rows_submatrix()
even_rows_leftmost_minimums_indices = __monge_leftmost_minimums_indices(A_, m // 2, n)
odd_rows_leftmost_minimums_indices = __monge_odd_rows_leftmost_minimums_indices(A, m, n,
even_rows_leftmost_minimums_indices)
indices = Array(1, m)
indices = Array[int](1, m)
for i in range_of(1, to=m // 2):
indices[2 * i] = even_rows_leftmost_minimums_indices[i]
for i in range_of(1, to=ceil_div(m, 2)):
indices[2 * i - 1] = odd_rows_leftmost_minimums_indices[i]
return indices


def __monge_odd_rows_leftmost_minimums_indices(A: Matrix, m: int, n: int,
even_rows_leftmost_minimums_indices: Array[Union[int, float]]) \
-> Array[Union[int, float]]:
def __monge_odd_rows_leftmost_minimums_indices(A: Matrix,
m: int,
n: int,
even_rows_leftmost_minimums_indices: Array[int])\
-> Array[int | float]:
odd_rows_leftmost_minimums_indices = Array(1, ceil_div(m, 2))
for i in range_of(1, to=ceil_div(m, 2)):
prev_minimum_index = even_rows_leftmost_minimums_indices[i - 1] if i > 1 else 1
Expand Down
6 changes: 3 additions & 3 deletions src/solutions/chapter5/problem2.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from typing import Optional
from __future__ import annotations

from book.data_structures import Array
from book.data_structures import T
from solutions.chapter5.section1.exercise2 import random
from util import range_of


def random_search(A: Array[T], n: int, x: T) -> Optional[int]:
def random_search(A: Array[T], n: int, x: T) -> int | None:
"""Searches for an element in a sequence of elements by randomly choosing next element to check.
Implements:
Expand All @@ -20,7 +20,7 @@ def random_search(A: Array[T], n: int, x: T) -> Optional[int]:
Returns:
An index i such that x equals A[i] or None if x does not appear in A.
"""
picked = Array(1, n)
picked = Array[bool](1, n)
for i in range_of(1, to=n):
picked[i] = False
k = 0
Expand Down
16 changes: 10 additions & 6 deletions src/util.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
from __future__ import annotations

from collections.abc import Iterable
from typing import Union


def range_of(_from: int, to: int = None, downto: int = None, by: int = None) -> Iterable:
if downto is None:
def range_of(_from: int,
to: int | None = None,
downto: int | None = None,
by: int | None = None) -> Iterable:
if to is not None and downto is None:
return range(_from, to + 1, 1 if by is None else by)
if to is None and by is None:
if to is None and downto is not None and by is None:
return range(_from, downto - 1, -1)
AssertionError('Invalid range bounds')
raise AssertionError('Invalid range bounds')


def ceil_div(x: Union[int, float], y: Union[int, float]) -> int:
def ceil_div(x: int, y: int) -> int:
return -(x // -y)
4 changes: 2 additions & 2 deletions test/test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

class ClrsTestCase(unittest.TestCase):

def assertArraySorted(self, array: Array[CT], start: int = 1, end: int = None,
def assertArraySorted(self, array: Array[CT], end: int, start: int = 1,
cmp: Callable[[Any, Any], bool] = operator.le) -> None:
if start >= end:
return
Expand All @@ -22,7 +22,7 @@ def assertArraySorted(self, array: Array[CT], start: int = 1, end: int = None,
self.fail(f'Array {array} is not sorted according to the relation {cmp}: '
f'elements ${array[i]}, ${array[i + 1]} are out of order')

def assertArrayPermuted(self, array: Array[T], elements: list[T], start: int = 1, end: int = None) -> None:
def assertArrayPermuted(self, array: Array[T], elements: list[T], end: int, start: int = 1) -> None:
msg = f'Array {array} is not a permutation of {elements}'
if end - start + 1 != len(elements):
self.fail(msg)
Expand Down
4 changes: 2 additions & 2 deletions test/test_util.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Union
from __future__ import annotations

from book.data_structures import Array
from book.data_structures import Bit
Expand All @@ -17,7 +17,7 @@ def create_array(elements: list[T], start: int = 1) -> Array[T]:
return array


def create_matrix(elements: list[list[Union[int, float]]]) -> Matrix:
def create_matrix(elements: list[list[int | float]]) -> Matrix:
rows = len(elements)
cols = len(elements[0])
matrix = Matrix(rows, cols)
Expand Down

0 comments on commit c057e8f

Please sign in to comment.