Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add documentation
  • Loading branch information
rhettinger committed May 31, 2020
commit 9c9b27510c72fc07aacc6f3bef1922b37c557db7
110 changes: 86 additions & 24 deletions Doc/library/bisect.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ example of the algorithm (the boundary conditions are already right!).
The following functions are provided:


.. function:: bisect_left(a, x, lo=0, hi=len(a))
.. function:: bisect_left(a, x, lo=0, hi=len(a), *, key=None)

Locate the insertion point for *x* in *a* to maintain sorted order.
The parameters *lo* and *hi* may be used to specify a subset of the list
Expand All @@ -34,7 +34,15 @@ The following functions are provided:
that ``all(val < x for val in a[lo:i])`` for the left side and
``all(val >= x for val in a[i:hi])`` for the right side.

.. function:: bisect_right(a, x, lo=0, hi=len(a))
*key* specifies a :term:`key function` of one argument that is used to
extract a comparison key from each input element. The default value is
``None`` (compare the elements directly).

.. versionchanged:: 3.10
Added the *key* parameter.


.. function:: bisect_right(a, x, lo=0, hi=len(a), *, key=None)
bisect(a, x, lo=0, hi=len(a))

Similar to :func:`bisect_left`, but returns an insertion point which comes
Expand All @@ -44,26 +52,85 @@ The following functions are provided:
that ``all(val <= x for val in a[lo:i])`` for the left side and
``all(val > x for val in a[i:hi])`` for the right side.

.. function:: insort_left(a, x, lo=0, hi=len(a))
*key* specifies a :term:`key function` of one argument that is used to
extract a comparison key from each input element. The default value is
``None`` (compare the elements directly).

.. versionchanged:: 3.10
Added the *key* parameter.


.. function:: insort_left(a, x, lo=0, hi=len(a), *, key=None)

Insert *x* in *a* in sorted order. This is equivalent to
``a.insert(bisect.bisect_left(a, x, lo, hi), x)`` assuming that *a* is
already sorted. Keep in mind that the O(log n) search is dominated by
the slow O(n) insertion step.
Insert *x* in *a* in sorted order.

.. function:: insort_right(a, x, lo=0, hi=len(a))
*key* specifies a :term:`key function` of one argument that is used to
extract a comparison key from each input element. The default value is
``None`` (compare the elements directly).

This function first runs :func:`bisect_left` to locate an insertion point.
Next, it runs the :meth:`insert` method on *a* to insert *x* at the
appropriate position to maintain sort order.

Keep in mind that the ``O(log n)`` search is dominated by the slow O(n)
insertion step.

.. versionchanged:: 3.10
Added the *key* parameter.


.. function:: insort_right(a, x, lo=0, hi=len(a), *, key=None)
insort(a, x, lo=0, hi=len(a))

Similar to :func:`insort_left`, but inserting *x* in *a* after any existing
entries of *x*.

*key* specifies a :term:`key function` of one argument that is used to
extract a comparison key from each input element. The default value is
``None`` (compare the elements directly).

This function first runs :func:`bisect_right` to locate an insertion point.
Next, it runs the :meth:`insert` method on *a* to insert *x* at the
appropriate position to maintain sort order.

Keep in mind that the ``O(log n)`` search is dominated by the slow O(n)
insertion step.

.. versionchanged:: 3.10
Added the *key* parameter.


Performance Notes
-----------------

When writing time sensitive code using *bisect()* and *insort()*, keep these
thoughts in mind:

* Bisection is effective for searching ranges of values.
For locating specific values, dictionaries are more performant.

* The *insort()* functions are ``O(n)`` because the logarithmic search step
is dominated by the linear time insertion step.

* The search functions are stateless and discard key function results after
they are used. Consequently, if the search functions are used a loop, the
Comment thread
rhettinger marked this conversation as resolved.
Outdated
key function may be called again and again on the same array elements. If
the key function isn't fast, consider wrapping it with
:func:`functools.cache` to avoid duplicate computations. Alternatively,
consider searching an array of precomputed the keys to locate the insertion
Comment thread
rhettinger marked this conversation as resolved.
Outdated
point (as shown in the examples section below).

.. seealso::

`SortedCollection recipe
<https://code.activestate.com/recipes/577197-sortedcollection/>`_ that uses
bisect to build a full-featured collection class with straight-forward search
methods and support for a key-function. The keys are precomputed to save
unnecessary calls to the key function during searches.
* `Sorted Collections
<http://www.grantjenks.com/docs/sortedcollections/>`_ is a high performance
module that uses *bisect* to managed sorted collections of data.

* The `SortedCollection recipe
<https://code.activestate.com/recipes/577197-sortedcollection/>`_ uses
bisect to build a full-featured collection class with straight-forward search
methods and support for a key-function. The keys are precomputed to save
unnecessary calls to the key function during searches.


Searching Sorted Lists
Expand Down Expand Up @@ -110,8 +177,8 @@ lists::
raise ValueError


Other Examples
--------------
Examples
--------

.. _bisect-example:

Expand All @@ -127,17 +194,12 @@ a 'B', and so on::
>>> [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]]
['F', 'A', 'C', 'C', 'B', 'A', 'A']

Unlike the :func:`sorted` function, it does not make sense for the :func:`bisect`
functions to have *key* or *reversed* arguments because that would lead to an
inefficient design (successive calls to bisect functions would not "remember"
all of the previous key lookups).

Instead, it is better to search a list of precomputed keys to find the index
of the record in question::
One technique to avoid repeated calls to a key function is to search a list of
precomputed keys to find the index of a record::

>>> data = [('red', 5), ('blue', 1), ('yellow', 8), ('black', 0)]
>>> data.sort(key=lambda r: r[1])
>>> keys = [r[1] for r in data] # precomputed list of keys
>>> data.sort(key=lambda r: r[1]) # Or use operator.itemgetter(1).
>>> keys = [r[1] for r in data] # Precompute a list of keys.
>>> data[bisect_left(keys, 0)]
('black', 0)
>>> data[bisect_left(keys, 1)]
Expand Down