Skip to content

Commit

Permalink
#240 use generics whenever possible
Browse files Browse the repository at this point in the history
  • Loading branch information
wojtask committed Oct 6, 2024
1 parent e431c39 commit 28e2311
Show file tree
Hide file tree
Showing 28 changed files with 104 additions and 66 deletions.
3 changes: 2 additions & 1 deletion src/book/chapter2/problem2.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from book.data_structures import Array
from book.data_structures import CT
from util import range_of


def bubblesort(A: Array, n: int) -> None:
def bubblesort(A: Array[CT], n: int) -> None:
"""Sorts an array using bubblesort.
Implements:
Expand Down
2 changes: 1 addition & 1 deletion src/book/chapter2/problem3.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from util import range_of


def horner(A: Array, n: int, x: float) -> float:
def horner(A: Array[float], n: int, x: float) -> float:
"""Evaluates a polynomial for a given value using Horner's rule.
Implements:
Expand Down
5 changes: 3 additions & 2 deletions src/book/chapter2/section1.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from book.data_structures import Array
from book.data_structures import CT
from util import range_of


def insertion_sort(A: Array, n: int) -> None:
def insertion_sort(A: Array[CT], n: int) -> None:
"""Sorts an array using insertion sort.
Implements:
Expand All @@ -21,7 +22,7 @@ def insertion_sort(A: Array, n: int) -> None:
A[j + 1] = key


def sum_array(A: Array, n: int) -> int:
def sum_array(A: Array[int], n: int) -> int:
"""Computes the sum of numbers in a sequence.
Implements:
Expand Down
13 changes: 7 additions & 6 deletions src/book/chapter2/section3.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from book.data_structures import Array
from book.data_structures import CT
from util import range_of


def merge(A: Array, p: int, q: int, r: int) -> None:
def merge(A: Array[CT], p: int, q: int, r: int) -> None:
"""Merges two sorted subarrays into a single sorted subarray in place.
Implements:
Expand All @@ -16,8 +17,8 @@ def merge(A: Array, p: int, q: int, r: int) -> None:
"""
nL = q - p + 1
nR = r - q
L = Array(0, nL - 1)
R = Array(0, nR - 1)
L = Array[CT](0, nL - 1)
R = Array[CT](0, nR - 1)
for i in range_of(0, to=nL - 1):
L[i] = A[p + i]
for j in range_of(0, to=nR - 1):
Expand All @@ -43,7 +44,7 @@ def merge(A: Array, p: int, q: int, r: int) -> None:
k += 1


def merge_sort(A: Array, p: int, r: int) -> None:
def merge_sort(A: Array[CT], p: int, r: int) -> None:
"""Sorts an array using merge sort.
Implements:
Expand All @@ -62,7 +63,7 @@ def merge_sort(A: Array, p: int, r: int) -> None:
merge(A, p, q, r)


def insertion_sort_with_binary_search(A: Array, n: int) -> None:
def insertion_sort_with_binary_search(A: Array[CT], n: int) -> None:
"""Sorts an array using insertion sort with binary search instead of linear search.
Args:
Expand All @@ -78,7 +79,7 @@ def insertion_sort_with_binary_search(A: Array, n: int) -> None:
A[k] = key


def __binary_search_position(A: Array, x: int, p: int, r: int) -> int:
def __binary_search_position(A: Array[CT], x: int, p: int, r: int) -> int:
if p > r:
return p
q = (p + r) // 2
Expand Down
7 changes: 4 additions & 3 deletions src/book/chapter5/problem1.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from random import uniform

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


def increment(A: Array, b: int, count_sequence: Array) -> None:
def increment(A: Array[Bit], b: int, count_sequence: Array[int]) -> None:
"""Increments the counter in a probabilistic manner, according to the count sequence.
Implements:
Expand All @@ -18,15 +19,15 @@ def increment(A: Array, b: int, count_sequence: Array) -> None:
__binary_counter_increment(A, b)


def __compute_value(A: Array, b: int) -> int:
def __compute_value(A: Array[Bit], b: int) -> int:
value = 0
for i in range_of(0, to=b - 1):
value += A[i] * 2 ** i
return value


# TODO(#26) move to book.chapter16.section1
def __binary_counter_increment(A: Array, k: int) -> None:
def __binary_counter_increment(A: Array[Bit], k: int) -> None:
i = 0
while i < k and A[i] == 1:
A[i] = 0
Expand Down
9 changes: 5 additions & 4 deletions src/book/chapter5/problem2.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@

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, n: int, x: int) -> Optional[int]:
def deterministic_search(A: Array[T], n: int, x: T) -> Optional[int]:
"""Searches for a value in an array, examining its elements sequentially.
Implements:
Deterministic-Search
Args:
A: an Array to search through
n: the number of numbers in array A
n: the number of elements in array A
x: the value to search for
Returns:
Expand All @@ -25,15 +26,15 @@ def deterministic_search(A: Array, n: int, x: int) -> Optional[int]:
return None


def scramble_search(A: Array, n: int, x: int) -> Optional[int]:
def scramble_search(A: Array[T], n: int, x: T) -> Optional[int]:
"""Searches for a value in an array by first permuting it, then examining its elements sequentially.
Implements:
Scramble-Search
Args:
A: an Array to search through
n: the number of numbers in array A
n: the number of elements in array A
x: the value to search for
Returns:
Expand Down
4 changes: 2 additions & 2 deletions src/book/chapter5/section1.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@


# Added the implicit rank array to the list of parameters
def hire_assistant(rank: Array, n: int) -> None:
def hire_assistant(rank: Array[int], n: int) -> None:
"""Interviews a set of candidates and hires the best qualified one as an office assistant.
Implements:
Expand All @@ -30,7 +30,7 @@ def __interview_candidate(i: int) -> None:
print('interviewing candidate ' + str(i))


def __compare_candidates(i: int, j: int, rank: Array) -> bool:
def __compare_candidates(i: int, j: int, rank: Array[int]) -> bool:
return j == 0 or rank[i] > rank[j]


Expand Down
13 changes: 7 additions & 6 deletions src/book/chapter5/section3.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from book.chapter5.section1 import hire_assistant
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 randomized_hire_assistant(rank: Array, n: int) -> None:
def randomized_hire_assistant(rank: Array[int], n: int) -> None:
"""Randomized version of Hire-Assistant.
Implements:
Expand All @@ -18,7 +19,7 @@ def randomized_hire_assistant(rank: Array, n: int) -> None:
hire_assistant(rank, n)


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


def permute_without_identity(A: Array, n: int) -> None:
def permute_without_identity(A: Array[int], 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 All @@ -52,7 +53,7 @@ def permute_without_identity(A: Array, n: int) -> None:
A[i], A[j] = A[j], A[i]


def permute_with_all(A: Array, n: int) -> None:
def permute_with_all(A: Array[T], n: int) -> None:
"""Permutes an array in place. Can't produce a uniform random permutation.
Implements:
Expand All @@ -68,7 +69,7 @@ def permute_with_all(A: Array, n: int) -> None:
A[i], A[j] = A[j], A[i]


def permute_by_cycle(A: Array, n: int) -> Array:
def permute_by_cycle(A: Array[T], n: int) -> Array[T]:
"""Permutes an array in place. Can't produce a uniform random permutation.
Implements:
Expand All @@ -81,7 +82,7 @@ def permute_by_cycle(A: Array, n: int) -> Array:
Returns:
The permuted array.
"""
B = Array(1, n)
B = Array[T](1, n)
offset = random(1, n)
for i in range_of(1, to=n):
dest = i + offset
Expand Down
2 changes: 1 addition & 1 deletion src/book/chapter5/section4.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@


# Added the implicit score array to the list of parameters
def online_maximum(score: Array, k: int, n: int) -> int:
def online_maximum(score: Array[int], k: int, n: int) -> int:
"""An online version of the algorithm to find the best candidate.
Implements:
Expand Down
16 changes: 16 additions & 0 deletions src/book/data_structures.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
from builtins import len
from collections.abc import Callable
from typing import Any
from typing import Literal
from typing import TypeVar
from typing import Union

from typing_extensions import Protocol
from typing_extensions import Self


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


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


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


class Array[T]:
Expand Down
5 changes: 3 additions & 2 deletions src/solutions/chapter2/problem1.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from book.chapter2.section3 import merge
from book.data_structures import Array
from book.data_structures import CT
from util import range_of


def merge_sort_with_insertion_sort(A: Array, p: int, r: int, k: int) -> None:
def merge_sort_with_insertion_sort(A: Array[CT], p: int, r: int, k: int) -> None:
"""Sorts an array using merge sort, while delegating small subproblems to insertion sort.
Args:
Expand All @@ -22,7 +23,7 @@ def merge_sort_with_insertion_sort(A: Array, p: int, r: int, k: int) -> None:
merge(A, p, q, r)


def __insertion_sort_sublist(A: Array, p: int, r: int) -> None:
def __insertion_sort_sublist(A: Array[CT], p: int, r: int) -> None:
for i in range_of(p + 1, to=r):
key = A[i]
j = i - 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 @@ -2,7 +2,7 @@
from util import range_of


def polynomial_evaluate(A: Array, n: int, x: float) -> float:
def polynomial_evaluate(A: Array[float], n: int, x: float) -> float:
"""Evaluates a polynomial for a given value using the naive method.
Implements:
Expand Down
9 changes: 5 additions & 4 deletions src/solutions/chapter2/problem4.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from book.data_structures import Array
from book.data_structures import CT
from util import range_of


def merge_inversions(A: Array, p: int, q: int, r: int) -> int:
def merge_inversions(A: Array[CT], p: int, q: int, r: int) -> int:
"""Merges two sorted subarrays to form a single sorted subarray in place, and counts inversions.
Args:
Expand All @@ -16,8 +17,8 @@ def merge_inversions(A: Array, p: int, q: int, r: int) -> int:
"""
nL = q - p + 1
nR = r - q
L = Array(0, nL - 1)
R = Array(0, nR - 1)
L = Array[CT](0, nL - 1)
R = Array[CT](0, nR - 1)
for i in range_of(0, to=nL - 1):
L[i] = A[p + i]
for j in range_of(0, to=nR - 1):
Expand Down Expand Up @@ -46,7 +47,7 @@ def merge_inversions(A: Array, p: int, q: int, r: int) -> int:
return inversions


def count_inversions(A: Array, p: int, r: int) -> int:
def count_inversions(A: Array[CT], p: int, r: int) -> int:
"""Counts inversions in an array. Sorts the input array as a side effect.
Implements:
Expand Down
3 changes: 2 additions & 1 deletion src/solutions/chapter2/section1/exercise3.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from book.data_structures import Array
from book.data_structures import CT
from util import range_of


def insertion_sort_decreasing(A: Array, n: int) -> None:
def insertion_sort_decreasing(A: Array[CT], n: int) -> None:
"""Sorts an array into monotonically decreasing order using insertion sort.
Args:
Expand Down
3 changes: 2 additions & 1 deletion src/solutions/chapter2/section1/exercise4.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from typing import Optional

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


def linear_search(A: Array, n: int, x: int) -> Optional[int]:
def linear_search(A: Array[T], n: int, x: int) -> Optional[int]:
"""Searches for a number in a sequence of numbers using linear search.
Implements:
Expand Down
5 changes: 3 additions & 2 deletions src/solutions/chapter2/section1/exercise5.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from book.data_structures import Array
from book.data_structures import Bit
from util import range_of


def add_binary_integers(A: Array, B: Array, n: int) -> Array:
def add_binary_integers(A: Array[Bit], B: Array[Bit], n: int) -> Array[Bit]:
"""Adds two integers, a and b, given as binary representations.
Implements:
Expand All @@ -16,7 +17,7 @@ def add_binary_integers(A: Array, B: Array, n: int) -> Array:
Returns:
The 0-origin indexed (n+1)-element Array containing the binary representation of a + b.
"""
C = Array(0, n)
C = Array[Bit](0, n)
carry = 0
for i in range_of(0, to=n - 1):
C[i] = (A[i] + B[i] + carry) % 2
Expand Down
3 changes: 2 additions & 1 deletion src/solutions/chapter2/section2/exercise2.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from book.data_structures import Array
from book.data_structures import CT
from util import range_of


def selection_sort(A: Array, n: int) -> None:
def selection_sort(A: Array[CT], n: int) -> None:
"""Sorts an array using selection sort.
Implements:
Expand Down
3 changes: 2 additions & 1 deletion src/solutions/chapter2/section3/exercise5.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from book.data_structures import Array
from book.data_structures import CT


def recursive_insertion_sort(A: Array, n: int) -> None:
def recursive_insertion_sort(A: Array[CT], n: int) -> None:
"""Sorts an array using a recursive version of insertion sort.
Implements:
Expand Down
Loading

0 comments on commit 28e2311

Please sign in to comment.