diff --git a/sorts/recursive_insertion_sort.py b/sorts/recursive_insertion_sort.py index b1df234ebef4..7076444921c0 100644 --- a/sorts/recursive_insertion_sort.py +++ b/sorts/recursive_insertion_sort.py @@ -4,14 +4,24 @@ from __future__ import annotations +from collections.abc import MutableSequence +from typing import Any, Protocol, TypeVar -def rec_insertion_sort(collection: list, n: int) -> None: + +class Comparable(Protocol): + def __lt__(self, other: Any, /) -> bool: ... + + +T = TypeVar("T", bound=Comparable) + + +def rec_insertion_sort(collection: MutableSequence[T], n: int) -> None: """ - Given a collection of numbers and its length, sorts the collections - in ascending order + Given a collection of comparable elements and its length, sorts the + collection in place in ascending order. :param collection: A mutable collection of comparable elements - :param n: The length of collections + :param n: The length of collection >>> col = [1, 2, 1] >>> rec_insertion_sort(col, len(col)) @@ -27,6 +37,11 @@ def rec_insertion_sort(collection: list, n: int) -> None: >>> rec_insertion_sort(col, len(col)) >>> col [1] + + >>> col = ['d', 'a', 'b', 'e', 'c'] + >>> rec_insertion_sort(col, len(col)) + >>> col + ['a', 'b', 'c', 'd', 'e'] """ # Checks if the entire collection has been sorted if len(collection) <= 1 or n <= 1: @@ -36,7 +51,7 @@ def rec_insertion_sort(collection: list, n: int) -> None: rec_insertion_sort(collection, n - 1) -def insert_next(collection: list, index: int) -> None: +def insert_next(collection: MutableSequence[T], index: int) -> None: """ Inserts the '(index-1)th' element into place diff --git a/tests/test_sorts.py b/tests/test_sorts.py index 2c9b79aa4bfe..b7328d500d76 100644 --- a/tests/test_sorts.py +++ b/tests/test_sorts.py @@ -12,6 +12,9 @@ ``bead_sort`` needs non-negative integers, ``dutch_national_flag_sort`` expects 0/1/2, ``bitonic_sort`` needs a power-of-two length, ``topological_sort`` works on a graph, and ``stalin_sort``/``wiggle_sort`` deliberately do not fully sort). +``rec_insertion_sort`` is also left out of the battery: it sorts in place and +returns ``None`` rather than the sorted collection, so it is exercised +separately below. """ from dataclasses import dataclass @@ -35,6 +38,7 @@ from sorts.odd_even_sort import odd_even_sort from sorts.patience_sort import patience_sort from sorts.quick_sort import quick_sort +from sorts.recursive_insertion_sort import rec_insertion_sort from sorts.selection_sort import selection_sort from sorts.shell_sort import shell_sort from sorts.stooge_sort import stooge_sort @@ -108,6 +112,14 @@ def test_sort_matches_builtin(sort, case) -> None: assert list(sort(list(case))) == sorted(case) +@pytest.mark.parametrize("case", CASES, ids=repr) +def test_rec_insertion_sort(case) -> None: + """``rec_insertion_sort`` sorts in place and returns ``None``.""" + collection = list(case) + assert rec_insertion_sort(collection, len(collection)) is None + assert collection == sorted(case) + + @pytest.mark.parametrize( "sort", [ @@ -128,3 +140,8 @@ def test_sort_matches_builtin(sort, case) -> None: def test_sort_rejects_non_comparable_items(sort) -> None: with pytest.raises(TypeError): sort([1, "a"]) + + +def test_rec_insertion_sort_rejects_non_comparable_items() -> None: + with pytest.raises(TypeError): + rec_insertion_sort([1, "a"], 2)