Skip to content

Commit 9ec2f09

Browse files
Rokesh2008cclauss
andauthored
sorts: make recursive_insertion_sort generic over Comparable items (#15340)
* sorts: make recursive_insertion_sort generic over Comparable items Part of #15234 - switch rec_insertion_sort/insert_next to the Comparable/TypeVar-bound MutableSequence[T] pattern (in-place sorts bucket) per the convention discussed on #15234 - make rec_insertion_sort return the sorted collection and give n a default of len(collection), so it can be called with a single argument like the other sorts in the shared test battery - add a string doctest - register rec_insertion_sort in tests/test_sorts.py's shared SORTS battery and the non-comparable-items rejection test * fix: keep rec_insertion_sort in-place with None return, per maintainer review - Revert n to a required parameter and drop the MutableSequence[T] return value; rec_insertion_sort stays a pure in-place sort returning None, as requested in review. - Keep the Comparable/TypeVar generalization (no PEP 695 syntax, to match the existing TypeVar style in the file). - Keep the non-int (string) doctest. - tests/test_sorts.py: rec_insertion_sort no longer fits the shared SORTS battery (which asserts on a returned value), so it's removed from that tuple and given its own parametrized in-place test, plus its own non-comparable-items rejection test. * test: adjust test_sorts.py for rec_insertion_sort's None-returning in-place contract rec_insertion_sort no longer fits the shared SORTS battery (which asserts on a returned value), so it's removed from that tuple and given its own parametrized in-place test (checked against sorted()) plus its own non-comparable-items rejection test. * Apply batched suggestions from code review Co-authored-by: Christian Clauss <cclauss@me.com> --------- Co-authored-by: Christian Clauss <cclauss@me.com>
1 parent 219478d commit 9ec2f09

2 files changed

Lines changed: 37 additions & 5 deletions

File tree

‎sorts/recursive_insertion_sort.py‎

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,24 @@
44

55
from __future__ import annotations
66

7+
from collections.abc import MutableSequence
8+
from typing import Any, Protocol, TypeVar
79

8-
def rec_insertion_sort(collection: list, n: int) -> None:
10+
11+
class Comparable(Protocol):
12+
def __lt__(self, other: Any, /) -> bool: ...
13+
14+
15+
T = TypeVar("T", bound=Comparable)
16+
17+
18+
def rec_insertion_sort[T](collection: MutableSequence[T], n: int) -> None:
919
"""
10-
Given a collection of numbers and its length, sorts the collections
11-
in ascending order
20+
Given a collection of comparable elements and its length, sorts the
21+
collection in place in ascending order.
1222
1323
:param collection: A mutable collection of comparable elements
14-
:param n: The length of collections
24+
:param n: The length of collection
1525
1626
>>> col = [1, 2, 1]
1727
>>> rec_insertion_sort(col, len(col))
@@ -27,6 +37,11 @@ def rec_insertion_sort(collection: list, n: int) -> None:
2737
>>> rec_insertion_sort(col, len(col))
2838
>>> col
2939
[1]
40+
41+
>>> col = ['d', 'a', 'b', 'e', 'c']
42+
>>> rec_insertion_sort(col, len(col))
43+
>>> col
44+
['a', 'b', 'c', 'd', 'e']
3045
"""
3146
# Checks if the entire collection has been sorted
3247
if len(collection) <= 1 or n <= 1:
@@ -36,7 +51,7 @@ def rec_insertion_sort(collection: list, n: int) -> None:
3651
rec_insertion_sort(collection, n - 1)
3752

3853

39-
def insert_next(collection: list, index: int) -> None:
54+
def insert_next[T](collection: MutableSequence[T], index: int) -> None:
4055
"""
4156
Inserts the '(index-1)th' element into place
4257

‎tests/test_sorts.py‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
``bead_sort`` needs non-negative integers, ``dutch_national_flag_sort`` expects
1313
0/1/2, ``bitonic_sort`` needs a power-of-two length, ``topological_sort`` works
1414
on a graph, and ``stalin_sort``/``wiggle_sort`` deliberately do not fully sort).
15+
``rec_insertion_sort`` is also left out of the battery: it sorts in place and
16+
returns ``None`` rather than the sorted collection, so it is exercised
17+
separately below.
1518
"""
1619

1720
from dataclasses import dataclass
@@ -36,6 +39,7 @@
3639
from sorts.pancake_sort import pancake_sort
3740
from sorts.patience_sort import patience_sort
3841
from sorts.quick_sort import quick_sort
42+
from sorts.recursive_insertion_sort import rec_insertion_sort
3943
from sorts.selection_sort import selection_sort
4044
from sorts.shell_sort import shell_sort
4145
from sorts.shrink_shell_sort import shell_sort as shrink_shell_sort
@@ -112,6 +116,14 @@ def test_sort_matches_builtin(sort, case) -> None:
112116
assert list(sort(list(case))) == sorted(case)
113117

114118

119+
@pytest.mark.parametrize("case", CASES, ids=repr)
120+
def test_rec_insertion_sort(case) -> None:
121+
"""``rec_insertion_sort`` sorts in place and returns ``None``."""
122+
collection = list(case)
123+
assert rec_insertion_sort(collection, len(collection)) is None
124+
assert collection == sorted(case)
125+
126+
115127
@pytest.mark.parametrize(
116128
"sort",
117129
[
@@ -136,3 +148,8 @@ def test_sort_matches_builtin(sort, case) -> None:
136148
def test_sort_rejects_non_comparable_items(sort) -> None:
137149
with pytest.raises(TypeError):
138150
sort([1, "a"])
151+
152+
153+
def test_rec_insertion_sort_rejects_non_comparable_items() -> None:
154+
with pytest.raises(TypeError):
155+
rec_insertion_sort([1, "a"], 2)

0 commit comments

Comments
 (0)