11from __future__ import annotations
2+
23from bisect import bisect_left
34from functools import total_ordering
45from heapq import merge
56
67"""
7- A pure Python implementation of the patience sort algorithm
8+ A pure Python implementation of the patience sort algorithm.
89
910For more information: https://en.wikipedia.org/wiki/Patience_sorting
1011
11- This algorithm is based on the card game patience
12+ This algorithm is based on the card game patience.
13+
14+ This version can sort integers, floats, and strings using
15+ mixed_patience_sort().
1216
13- For doctests run following command:
17+ For doctests run the following command:
1418python3 -m doctest -v patience_sort.py
1519
1620For manual testing run:
1721python3 patience_sort.py
1822"""
1923
24+
2025@total_ordering
2126class Stack (list ):
2227 def __lt__ (self , other ):
@@ -25,13 +30,11 @@ def __lt__(self, other):
2530 def __eq__ (self , other ):
2631 return self [- 1 ] == other [- 1 ]
2732
28- def patience_sort (collection : list ) -> list :
29- """A pure implementation of patience sort algorithm in Python
30-
31- :param collection: some mutable ordered collection with heterogeneous
32- comparable items inside
33- :return: the same collection ordered by ascending
3433
34+ def patience_sort (collection : list ) -> list :
35+ """Sort a collection using the patience sort algorithm.
36+ :param collection: A mutable ordered collection containing comparable items.
37+ :return: The same collection ordered in ascending order.
3538 Examples:
3639 >>> patience_sort([1, 9, 5, 21, 17, 6])
3740 [1, 5, 6, 9, 17, 21]
@@ -43,47 +46,87 @@ def patience_sort(collection: list) -> list:
4346 [-48, -17, -3]
4447 """
4548 stacks : list [Stack ] = []
46- # sort into stacks
49+
50+ # Place each element on the first suitable stack, following
51+ # the rules of the patience sort algorithm.
4752 for element in collection :
48- new_stacks = Stack ([element ])
49- i = bisect_left (stacks , new_stacks )
50- if i != len (stacks ):
51- stacks [i ].append (element )
53+ new_stack : list = Stack ([element ])
54+ stack_index : list = bisect_left (stacks , new_stack )
55+
56+ if stack_index != len (stacks ):
57+ stacks [stack_index ].append (element )
5258 else :
53- stacks .append (new_stacks )
54-
55- # use a heap-based merge to merge stack efficiently
59+ stacks .append (new_stack )
60+
61+ # Merge the stacks to produce the final sorted collection.
5662 collection [:] = merge (* (reversed (stack ) for stack in stacks ))
5763 return collection
58-
64+
65+
5966def mixed_patience_sort (collection : list ) -> list :
60- """
61- With this block of code, numbers are sorted according to their
62- magnitude, the strings are arranged in alphbetical order. And the numbers
63- are arranged before the alphabets.
64- """
67+ """Sort integers, floats, and strings using patience sort.
68+
69+ Numbers are sorted by magnitude and placed before strings.
70+ Strings are sorted alphabetically according to Python's
71+ standard string ordering.
6572
66- number_items = []
67- string_items = []
73+ :param collection: A mutable collection containing integers,
74+ floats, and strings.
75+ :return: The same collection with numbers followed by strings.
76+
77+ Examples:
78+ >>> mixed_patience_sort([1, 9, 3.64, "Apple", "letter", 23.65])
79+ [1, 3.64, 9, 23.65, 'Apple', 'letter']
80+
81+ >>> mixed_patience_sort(["banana", "Apple", "letter"])
82+ ['Apple', 'banana', 'letter']
83+
84+ >>> mixed_patience_sort([3.5, 1, 8.2])
85+ [1, 3.5, 8.2]
86+
87+ >>> mixed_patience_sort([])
88+ []
89+
90+ >>> mixed_patience_sort([1, {"name": "Obed"}])
91+ Traceback (most recent call last):
92+ ValueError: Only integers, floats, and strings are allowed.
93+ """
94+ number_items : list [int | float ] = []
95+ string_items : list [str ] = []
6896
6997 for element in collection :
7098 if isinstance (element , (int , float )):
7199 number_items .append (element )
72100 elif isinstance (element , str ):
73101 string_items .append (element )
74102 else :
75- print ("Only integers, floats, and strings are allowed." )
103+ raise ValueError ("Only integers, floats, and strings are allowed." )
76104
77- # The user input is sort by using the patience sort algorithm
78- sorted_numbers = patience_sort (number_items )
79- sorted_strings = patience_sort (string_items )
105+ sorted_numbers : int = patience_sort (number_items )
106+ sorted_strings : str = patience_sort (string_items )
80107
81- # use a heap-based merge to merge stack efficiently
82108 collection [:] = sorted_numbers + sorted_strings
83109 return collection
84110
85- def convert_item (item ):
111+
112+ def convert_item (item : str ) -> int | float | str :
113+ """Convert a string into an integer, float, or string.
114+
115+ :param item: The input string to convert.
116+ :return: An integer, float, or the original string.
117+
118+ Examples:
119+ >>> convert_item("25")
120+ 25
121+
122+ >>> convert_item("3.14")
123+ 3.14
124+
125+ >>> convert_item("Apple")
126+ 'Apple'
127+ """
86128 item = item .strip ()
129+
87130 try :
88131 return int (item )
89132 except ValueError :
@@ -92,7 +135,8 @@ def convert_item(item):
92135 except ValueError :
93136 return item
94137
138+
95139if __name__ == "__main__" :
96- user_input = input ("Enter numbers separated by a comma :\n " ).strip ()
97- unsorted = [convert_item (item ) for item in user_input .split ("," )]
140+ user_input : str | int | float = input ("Enter integers, floats, and strings separated by commas :\n " ).strip ()
141+ unsorted : str | int | float = [convert_item (item ) for item in user_input .split ("," )]
98142 print (mixed_patience_sort (unsorted ))
0 commit comments