Skip to content

Commit 36cdad1

Browse files
committed
Fred, THIS NEEDS DOCS! The function docstrings tell the tale.
Christmas present to myself: the bisect module didn't define what happened if the new element was already in the list. It so happens that it inserted the new element "to the right" of all equal elements. Since it wasn't defined, among other bad implications it was a mystery how to use bisect to determine whether an element was already in the list (I've seen code that *assumed* "to the right" without justification). Added new methods bisect_left and insort_left that insert "to the left" instead; made the old names bisect and insort aliases for the new names bisect_right and insort_right; beefed up docstrings to explain what these actually do; and added a std test for the bisect module.
1 parent 011ea47 commit 36cdad1

File tree

4 files changed

+200
-4
lines changed

4 files changed

+200
-4
lines changed

Lib/bisect.py

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
"""Bisection algorithms."""
22

33

4-
def insort(a, x, lo=0, hi=None):
5-
"""Insert item x in list a, and keep it sorted assuming a is sorted."""
4+
def insort_right(a, x, lo=0, hi=None):
5+
"""Insert item x in list a, and keep it sorted assuming a is sorted.
6+
7+
If x is already in a, insert it to the right of the rightmost x.
8+
9+
Optional args lo (default 0) and hi (default len(a)) bound the
10+
slice of a to be searched.
11+
"""
12+
613
if hi is None:
714
hi = len(a)
815
while lo < hi:
@@ -11,13 +18,62 @@ def insort(a, x, lo=0, hi=None):
1118
else: lo = mid+1
1219
a.insert(lo, x)
1320

21+
insort = insort_right # backward compatibility
22+
23+
def bisect_right(a, x, lo=0, hi=None):
24+
"""Return the index where to insert item x in list a, assuming a is sorted.
25+
26+
The return value i is such that all e in a[:i] have e <= x, and all e in
27+
a[i:] have e > x. So if x already appears in the list, i points just
28+
beyond the rightmost x already there.
29+
30+
Optional args lo (default 0) and hi (default len(a)) bound the
31+
slice of a to be searched.
32+
"""
1433

15-
def bisect(a, x, lo=0, hi=None):
16-
"""Find the index where to insert item x in list a, assuming a is sorted."""
1734
if hi is None:
1835
hi = len(a)
1936
while lo < hi:
2037
mid = (lo+hi)/2
2138
if x < a[mid]: hi = mid
2239
else: lo = mid+1
2340
return lo
41+
42+
bisect = bisect_right # backward compatibility
43+
44+
def insort_left(a, x, lo=0, hi=None):
45+
"""Insert item x in list a, and keep it sorted assuming a is sorted.
46+
47+
If x is already in a, insert it to the left of the leftmost x.
48+
49+
Optional args lo (default 0) and hi (default len(a)) bound the
50+
slice of a to be searched.
51+
"""
52+
53+
if hi is None:
54+
hi = len(a)
55+
while lo < hi:
56+
mid = (lo+hi)/2
57+
if a[mid] < x: lo = mid+1
58+
else: hi = mid
59+
a.insert(lo, x)
60+
61+
62+
def bisect_left(a, x, lo=0, hi=None):
63+
"""Return the index where to insert item x in list a, assuming a is sorted.
64+
65+
The return value i is such that all e in a[:i] have e < x, and all e in
66+
a[i:] have e >= x. So if x already appears in the list, i points just
67+
before the leftmost x already there.
68+
69+
Optional args lo (default 0) and hi (default len(a)) bound the
70+
slice of a to be searched.
71+
"""
72+
73+
if hi is None:
74+
hi = len(a)
75+
while lo < hi:
76+
mid = (lo+hi)/2
77+
if a[mid] < x: lo = mid+1
78+
else: hi = mid
79+
return lo

Lib/test/output/test_bisect

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test_bisect

Lib/test/test_bisect.py

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
from test_support import TestFailed
2+
3+
import bisect
4+
import sys
5+
6+
nerrors = 0
7+
8+
def check_bisect(func, list, elt, expected):
9+
global nerrors
10+
got = func(list, elt)
11+
if got != expected:
12+
print >> sys.stderr, \
13+
"expected %s(%s, %s) -> %s, but got %s" % (func.__name__,
14+
list,
15+
elt,
16+
expected,
17+
got)
18+
nerrors += 1
19+
20+
# XXX optional slice arguments need tests.
21+
22+
check_bisect(bisect.bisect_right, [], 1, 0)
23+
check_bisect(bisect.bisect_right, [1], 0, 0)
24+
check_bisect(bisect.bisect_right, [1], 1, 1)
25+
check_bisect(bisect.bisect_right, [1], 2, 1)
26+
check_bisect(bisect.bisect_right, [1, 1], 0, 0)
27+
check_bisect(bisect.bisect_right, [1, 1], 1, 2)
28+
check_bisect(bisect.bisect_right, [1, 1], 2, 2)
29+
check_bisect(bisect.bisect_right, [1, 1, 1], 0, 0)
30+
check_bisect(bisect.bisect_right, [1, 1, 1], 1, 3)
31+
check_bisect(bisect.bisect_right, [1, 1, 1], 2, 3)
32+
check_bisect(bisect.bisect_right, [1, 1, 1, 1], 0, 0)
33+
check_bisect(bisect.bisect_right, [1, 1, 1, 1], 1, 4)
34+
check_bisect(bisect.bisect_right, [1, 1, 1, 1], 2, 4)
35+
check_bisect(bisect.bisect_right, [1, 2], 0, 0)
36+
check_bisect(bisect.bisect_right, [1, 2], 1, 1)
37+
check_bisect(bisect.bisect_right, [1, 2], 1.5, 1)
38+
check_bisect(bisect.bisect_right, [1, 2], 2, 2)
39+
check_bisect(bisect.bisect_right, [1, 2], 3, 2)
40+
check_bisect(bisect.bisect_right, [1, 1, 2, 2], 0, 0)
41+
check_bisect(bisect.bisect_right, [1, 1, 2, 2], 1, 2)
42+
check_bisect(bisect.bisect_right, [1, 1, 2, 2], 1.5, 2)
43+
check_bisect(bisect.bisect_right, [1, 1, 2, 2], 2, 4)
44+
check_bisect(bisect.bisect_right, [1, 1, 2, 2], 3, 4)
45+
check_bisect(bisect.bisect_right, [1, 2, 3], 0, 0)
46+
check_bisect(bisect.bisect_right, [1, 2, 3], 1, 1)
47+
check_bisect(bisect.bisect_right, [1, 2, 3], 1.5, 1)
48+
check_bisect(bisect.bisect_right, [1, 2, 3], 2, 2)
49+
check_bisect(bisect.bisect_right, [1, 2, 3], 2.5, 2)
50+
check_bisect(bisect.bisect_right, [1, 2, 3], 3, 3)
51+
check_bisect(bisect.bisect_right, [1, 2, 3], 4, 3)
52+
check_bisect(bisect.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 0, 0)
53+
check_bisect(bisect.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 1, 1)
54+
check_bisect(bisect.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 1.5, 1)
55+
check_bisect(bisect.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 2, 3)
56+
check_bisect(bisect.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 2.5, 3)
57+
check_bisect(bisect.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 3, 6)
58+
check_bisect(bisect.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 3.5, 6)
59+
check_bisect(bisect.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 4, 10)
60+
check_bisect(bisect.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 5, 10)
61+
62+
check_bisect(bisect.bisect_left, [], 1, 0)
63+
check_bisect(bisect.bisect_left, [1], 0, 0)
64+
check_bisect(bisect.bisect_left, [1], 1, 0)
65+
check_bisect(bisect.bisect_left, [1], 2, 1)
66+
check_bisect(bisect.bisect_left, [1, 1], 0, 0)
67+
check_bisect(bisect.bisect_left, [1, 1], 1, 0)
68+
check_bisect(bisect.bisect_left, [1, 1], 2, 2)
69+
check_bisect(bisect.bisect_left, [1, 1, 1], 0, 0)
70+
check_bisect(bisect.bisect_left, [1, 1, 1], 1, 0)
71+
check_bisect(bisect.bisect_left, [1, 1, 1], 2, 3)
72+
check_bisect(bisect.bisect_left, [1, 1, 1, 1], 0, 0)
73+
check_bisect(bisect.bisect_left, [1, 1, 1, 1], 1, 0)
74+
check_bisect(bisect.bisect_left, [1, 1, 1, 1], 2, 4)
75+
check_bisect(bisect.bisect_left, [1, 2], 0, 0)
76+
check_bisect(bisect.bisect_left, [1, 2], 1, 0)
77+
check_bisect(bisect.bisect_left, [1, 2], 1.5, 1)
78+
check_bisect(bisect.bisect_left, [1, 2], 2, 1)
79+
check_bisect(bisect.bisect_left, [1, 2], 3, 2)
80+
check_bisect(bisect.bisect_left, [1, 1, 2, 2], 0, 0)
81+
check_bisect(bisect.bisect_left, [1, 1, 2, 2], 1, 0)
82+
check_bisect(bisect.bisect_left, [1, 1, 2, 2], 1.5, 2)
83+
check_bisect(bisect.bisect_left, [1, 1, 2, 2], 2, 2)
84+
check_bisect(bisect.bisect_left, [1, 1, 2, 2], 3, 4)
85+
check_bisect(bisect.bisect_left, [1, 2, 3], 0, 0)
86+
check_bisect(bisect.bisect_left, [1, 2, 3], 1, 0)
87+
check_bisect(bisect.bisect_left, [1, 2, 3], 1.5, 1)
88+
check_bisect(bisect.bisect_left, [1, 2, 3], 2, 1)
89+
check_bisect(bisect.bisect_left, [1, 2, 3], 2.5, 2)
90+
check_bisect(bisect.bisect_left, [1, 2, 3], 3, 2)
91+
check_bisect(bisect.bisect_left, [1, 2, 3], 4, 3)
92+
check_bisect(bisect.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 0, 0)
93+
check_bisect(bisect.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 1, 0)
94+
check_bisect(bisect.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 1.5, 1)
95+
check_bisect(bisect.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 2, 1)
96+
check_bisect(bisect.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 2.5, 3)
97+
check_bisect(bisect.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 3, 3)
98+
check_bisect(bisect.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 3.5, 6)
99+
check_bisect(bisect.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 4, 6)
100+
check_bisect(bisect.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 5, 10)
101+
102+
def check_insort(n):
103+
global nerrors
104+
from random import choice
105+
import sys
106+
digits = "0123456789"
107+
raw = []
108+
insorted = []
109+
for i in range(n):
110+
digit = choice(digits)
111+
raw.append(digit)
112+
if digit in "02468":
113+
f = bisect.insort_left
114+
else:
115+
f = bisect.insort_right
116+
f(insorted, digit)
117+
sorted = raw[:]
118+
sorted.sort()
119+
if sorted == insorted:
120+
return
121+
print >> sys.stderr, "insort test failed: raw %s got %s" % (raw, insorted)
122+
nerrors += 1
123+
124+
check_insort(500)
125+
126+
if nerrors:
127+
raise TestFailed("%d errors in test_bisect" % nerrors)

Misc/NEWS

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,18 @@ Core language, builtins, and interpreter
3434
item. Such algorithms normally end up running in quadratic time;
3535
using popitem() they can usually be made to run in linear time.
3636

37+
38+
Standard library
39+
40+
- The bisect module has new functions bisect_left, insort_left,
41+
bisect_right and insort_right. The old names bisect and insort
42+
are now aliases for bisect_right and insort_right. XXX_right
43+
and XXX_left methods differ in what happens when the new element
44+
compares equal to one or more elements already in the list: the
45+
XXX_left methods insert to the left, the XXX_right methods to the
46+
right.
47+
48+
3749
Windows changes
3850

3951
- select module: By default under Windows, a select() call

0 commit comments

Comments
 (0)