diff --git a/sorts/recursive_insertion_sort.py b/sorts/recursive_insertion_sort.py index 30630ce84694..15bb702790e1 100644 --- a/sorts/recursive_insertion_sort.py +++ b/sorts/recursive_insertion_sort.py @@ -5,17 +5,17 @@ from __future__ import annotations from collections.abc import MutableSequence -from typing import Any, Protocol, TypeVar +from typing import Protocol, TypeVar class Comparable(Protocol): - def __lt__(self, other: Any, /) -> bool: ... + def __lt__(self, other: object, /) -> bool: ... T = TypeVar("T", bound=Comparable) -def rec_insertion_sort[T](collection: MutableSequence[T], n: int) -> None: +def rec_insertion_sort[T: Comparable](collection: MutableSequence[T], n: int) -> None: """ Given a collection of comparable elements and its length, sorts the collection in place in ascending order. @@ -42,6 +42,17 @@ def rec_insertion_sort[T](collection: MutableSequence[T], n: int) -> None: >>> rec_insertion_sort(col, len(col)) >>> col ['a', 'b', 'c', 'd', 'e'] + + >>> col = [2.5, -1, 0.0, 1] + >>> rec_insertion_sort(col, len(col)) + >>> col + [-1, 0.0, 1, 2.5] + + >>> col = [1, "a"] + >>> rec_insertion_sort(col, len(col)) + Traceback (most recent call last): + ... + TypeError: '<' not supported between instances of 'str' and 'int' """ # Checks if the entire collection has been sorted if len(collection) <= 1 or n <= 1: @@ -51,7 +62,7 @@ def rec_insertion_sort[T](collection: MutableSequence[T], n: int) -> None: rec_insertion_sort(collection, n - 1) -def insert_next[T](collection: MutableSequence[T], index: int) -> None: +def insert_next[T: Comparable](collection: MutableSequence[T], index: int) -> None: """ Inserts the '(index-1)th' element into place @@ -71,7 +82,8 @@ def insert_next[T](collection: MutableSequence[T], index: int) -> None: [] """ # Checks order between adjacent elements - if index >= len(collection) or collection[index - 1] <= collection[index]: + # Only relies on ``__lt__`` so any Comparable works + if index >= len(collection) or not collection[index] < collection[index - 1]: return # Swaps adjacent elements since they are not in ascending order diff --git a/sorts/recursive_quick_sort.py b/sorts/recursive_quick_sort.py index c29009aca673..f0387d6377ab 100644 --- a/sorts/recursive_quick_sort.py +++ b/sorts/recursive_quick_sort.py @@ -1,18 +1,41 @@ -def quick_sort(data: list) -> list: +from __future__ import annotations + +from typing import Protocol, TypeVar + + +class Comparable(Protocol): + def __lt__(self, other: object, /) -> bool: ... + + +T = TypeVar("T", bound=Comparable) + + +def quick_sort[T: Comparable](data: list[T]) -> list[T]: """ >>> for data in ([2, 1, 0], [2.2, 1.1, 0], "quick_sort"): ... quick_sort(data) == sorted(data) True True True + + >>> quick_sort(["c", "a", "b"]) + ['a', 'b', 'c'] + + >>> quick_sort([2.5, -1, 0.0]) + [-1, 0.0, 2.5] + + >>> quick_sort([1, "a"]) + Traceback (most recent call last): + ... + TypeError: '<' not supported between instances of 'int' and 'str' """ if len(data) <= 1: return data else: return [ - *quick_sort([e for e in data[1:] if e <= data[0]]), + *quick_sort([e for e in data[1:] if not data[0] < e]), data[0], - *quick_sort([e for e in data[1:] if e > data[0]]), + *quick_sort([e for e in data[1:] if data[0] < e]), ] diff --git a/tests/test_sorts.py b/tests/test_sorts.py index df4fbbada4ec..2a419b6adc8f 100644 --- a/tests/test_sorts.py +++ b/tests/test_sorts.py @@ -48,6 +48,7 @@ from sorts.quick_sort_3_partition import three_way_radix_quicksort from sorts.recursive_insertion_sort import rec_insertion_sort from sorts.recursive_mergesort_array import merge +from sorts.recursive_quick_sort import quick_sort as recursive_quick_sort from sorts.reverse_selection import reverse_selection_sort from sorts.reversort import reversort from sorts.selection_sort import selection_sort @@ -117,6 +118,7 @@ def test_intro_sort_heap_fallback_preserves_surrounding_items(max_depth: int) -> pancake_sort, patience_sort, quick_sort, + recursive_quick_sort, reverse_selection_sort, reversort, selection_sort, @@ -196,6 +198,7 @@ def test_rec_insertion_sort(case) -> None: odd_even_transposition, pancake_sort, patience_sort, + recursive_quick_sort, reverse_selection_sort, reversort, selection_sort, @@ -217,6 +220,18 @@ def test_rec_insertion_sort_rejects_non_comparable_items() -> None: rec_insertion_sort([1, "a"], 2) +def test_recursive_quick_sort_mixed_types() -> None: + """``recursive_quick_sort`` handles comparable non-int items. + + It rejects mixed non-comparable types. + """ + assert recursive_quick_sort(["c", "a", "b"]) == ["a", "b", "c"] + assert recursive_quick_sort([2.5, -1, 0.0]) == [-1, 0.0, 2.5] + + with pytest.raises(TypeError): + recursive_quick_sort([1, "a"]) + + def test_bogo_sort_comparable_items() -> None: assert bogo_sort(["c", "a", "b"]) == ["a", "b", "c"] assert bogo_sort([2.5, -1.0, 0.0]) == [-1.0, 0.0, 2.5]