update from Josiah Carlson
This commit is contained in:
parent
57ec5c226a
commit
64497e6e43
341
pep-0326.txt
341
pep-0326.txt
|
@ -4,12 +4,24 @@ Version: $Revision$
|
||||||
Last-Modified: $Date$
|
Last-Modified: $Date$
|
||||||
Author: Josiah Carlson <jcarlson@uci.edu>,
|
Author: Josiah Carlson <jcarlson@uci.edu>,
|
||||||
Terry Reedy <tjreedy@udel.edu>
|
Terry Reedy <tjreedy@udel.edu>
|
||||||
Status: Draft
|
Status: Rejected
|
||||||
Type: Standards Track
|
Type: Standards Track
|
||||||
Content-Type: text/x-rst
|
Content-Type: text/x-rst
|
||||||
Created: 20-Dec-2003
|
Created: 20-Dec-2003
|
||||||
Python-Version: 2.4
|
Python-Version: 2.4
|
||||||
Post-History: 20-Dec-2003, 03-Jan-2004, 05-Jan-2004, 07-Jan-2004
|
Post-History: 20-Dec-2003, 03-Jan-2004, 05-Jan-2004, 07-Jan-2004,
|
||||||
|
21-Feb-2004
|
||||||
|
|
||||||
|
Results
|
||||||
|
=======
|
||||||
|
|
||||||
|
This PEP has been rejected by the BDFL [12]_. As per the
|
||||||
|
pseudo-sunset clause [13]_, PEP 326 is being updated one last time
|
||||||
|
with the latest suggestions, code modifications, etc., and includes a
|
||||||
|
link to a module [14]_ that implements the behavior described in the
|
||||||
|
PEP. Users who desire the behavior listed in this PEP are encouraged
|
||||||
|
to use the module for the reasons listed in
|
||||||
|
`Independent Implementations?`_.
|
||||||
|
|
||||||
|
|
||||||
Abstract
|
Abstract
|
||||||
|
@ -67,7 +79,7 @@ infinity). However, each has their drawbacks.
|
||||||
File "<stdin>", line 1, in ?
|
File "<stdin>", line 1, in ?
|
||||||
OverflowError: long int too large to convert to float
|
OverflowError: long int too large to convert to float
|
||||||
|
|
||||||
- These same drawbacks exist when numbers are small.
|
- These same drawbacks exist when numbers are negative.
|
||||||
|
|
||||||
Introducing ``Max`` and ``Min`` that work as described above does not
|
Introducing ``Max`` and ``Min`` that work as described above does not
|
||||||
take much effort. A sample Python `reference implementation`_ of both
|
take much effort. A sample Python `reference implementation`_ of both
|
||||||
|
@ -87,57 +99,23 @@ algorithms can become clearer due to the reduction of special cases.
|
||||||
``Max`` Examples
|
``Max`` Examples
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
Take for example, finding the minimum in a sequence::
|
When testing various kinds of servers, it is sometimes necessary to
|
||||||
|
only serve a certain number of clients before exiting, which results
|
||||||
|
in code like the following::
|
||||||
|
|
||||||
def findmin_Num(seq):
|
count = 5
|
||||||
BIG = 0
|
|
||||||
cur = BIG
|
|
||||||
for obj in seq:
|
|
||||||
if cur == BIG:
|
|
||||||
cur = obj
|
|
||||||
BIG = max(cur, BIG) + 1
|
|
||||||
else:
|
|
||||||
cur = min(cur, obj)
|
|
||||||
return cur
|
|
||||||
|
|
||||||
::
|
def counts(stop):
|
||||||
|
i = 0
|
||||||
|
while i < stop:
|
||||||
|
yield i
|
||||||
|
i += 1
|
||||||
|
|
||||||
def findmin_None(seq):
|
for client_number in counts(count):
|
||||||
cur = None
|
handle_one_client()
|
||||||
for obj in seq:
|
|
||||||
if obj < cur or (cur is None):
|
|
||||||
cur = obj
|
|
||||||
if cur is None:
|
|
||||||
return cur
|
|
||||||
return cur
|
|
||||||
|
|
||||||
::
|
When using ``Max`` as the value assigned to count, our testing server
|
||||||
|
becomes a production server with minimal effort.
|
||||||
def findmin_Max(seq):
|
|
||||||
cur = Max
|
|
||||||
for obj in seq:
|
|
||||||
cur = min(obj, cur)
|
|
||||||
return cur
|
|
||||||
|
|
||||||
Please note that there are an arbitrarily large number of ways to find
|
|
||||||
the minimum (or maximum) of a sequence, these seek to show a simple
|
|
||||||
example where using ``Max`` makes the algorithm easier to understand
|
|
||||||
and results in the simplification of code.
|
|
||||||
|
|
||||||
Guido brought up the idea of just negating everything and comparing
|
|
||||||
[2]_. Certainly this does work when using numbers, but it does not
|
|
||||||
remove the special case (actually adds one) and results in the code
|
|
||||||
being less readable. ::
|
|
||||||
|
|
||||||
#we have Max available
|
|
||||||
a = min(a, b)
|
|
||||||
|
|
||||||
#we don't have Max available
|
|
||||||
if a is not None:
|
|
||||||
if b is None:
|
|
||||||
a = b
|
|
||||||
else:
|
|
||||||
a = -max(-a, -b)
|
|
||||||
|
|
||||||
As another example, in Dijkstra's shortest path algorithm on a graph
|
As another example, in Dijkstra's shortest path algorithm on a graph
|
||||||
with weighted edges (all positive).
|
with weighted edges (all positive).
|
||||||
|
@ -156,63 +134,38 @@ with weighted edges (all positive).
|
||||||
5. If the destination has been visited, step back through parent
|
5. If the destination has been visited, step back through parent
|
||||||
pointers to find the reverse of the path to be taken.
|
pointers to find the reverse of the path to be taken.
|
||||||
|
|
||||||
To be complete, below are two versions of the algorithm, one using a
|
.. _DijkstraSP_table:
|
||||||
table (a bit more understandable) and one using a heap (much faster)::
|
|
||||||
|
Below is an example of Dijkstra's shortest path algorithm on a graph
|
||||||
|
with weighted edges using a table (a faster version that uses a heap
|
||||||
|
is available, but this version is offered due to its similarity to the
|
||||||
|
description above, the heap version is available via older versions of
|
||||||
|
this document through CVS [10]_). ::
|
||||||
|
|
||||||
def DijkstraSP_table(graph, S, T):
|
def DijkstraSP_table(graph, S, T):
|
||||||
#runs in O(N^2) time using a table
|
table = {} #3
|
||||||
#find the shortest path
|
|
||||||
table = {}
|
|
||||||
for node in graph.iterkeys():
|
for node in graph.iterkeys():
|
||||||
#(visited, distance, node, parent)
|
#(visited, distance, node, parent)
|
||||||
table[node] = (0, Max, node, None)
|
table[node] = (0, Max, node, None) #1
|
||||||
table[S] = (0, 0, S, None)
|
table[S] = (0, 0, S, None) #2
|
||||||
cur = min(table.values())
|
cur = min(table.values()) #4a
|
||||||
while (not cur[0]) and cur[1] < Max:
|
while (not cur[0]) and cur[1] < Max: #4
|
||||||
(visited, distance, node, parent) = cur
|
(visited, distance, node, parent) = cur
|
||||||
table[node] = (1, distance, node, parent)
|
table[node] = (1, distance, node, parent) #4b
|
||||||
for cdist, child in graph[node]:
|
for cdist, child in graph[node]: #4c
|
||||||
ndist = distance+cdist
|
ndist = distance+cdist #|
|
||||||
if not table[child][0] and ndist < table[child][1]:
|
if not table[child][0] and ndist < table[child][1]:#|
|
||||||
table[child] = (0, ndist, child, node)
|
table[child] = (0, ndist, child, node) #|_
|
||||||
cur = min(table.values())
|
cur = min(table.values()) #4a
|
||||||
#backtrace through results
|
|
||||||
if not table[T][0]:
|
if not table[T][0]:
|
||||||
return None
|
return None
|
||||||
cur = T
|
cur = T #5
|
||||||
path = [T]
|
path = [T] #|
|
||||||
while table[cur][3] is not None:
|
while table[cur][3] is not None: #|
|
||||||
path.append(table[cur][3])
|
path.append(table[cur][3]) #|
|
||||||
cur = path[-1]
|
cur = path[-1] #|
|
||||||
path.reverse()
|
path.reverse() #|
|
||||||
return path
|
return path #|_
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
def DijkstraSP_heap(graph, S, T):
|
|
||||||
#runs in O(NlgN) time using a minheap
|
|
||||||
#find the shortest path
|
|
||||||
import heapq
|
|
||||||
Q = [(Max, i, None) for i in graph.iterkeys()]
|
|
||||||
heapq.heappush(Q, (0, S, None))
|
|
||||||
V = {}
|
|
||||||
while Q[0][0] < Max and T not in V:
|
|
||||||
dist, node, parent = heapq.heappop(Q)
|
|
||||||
if node in V:
|
|
||||||
continue
|
|
||||||
V[node] = (dist, parent)
|
|
||||||
for next, dest in graph[node]:
|
|
||||||
heapq.heappush(Q, (next+dist, dest, node))
|
|
||||||
#backtrace through results
|
|
||||||
if T not in V:
|
|
||||||
return None
|
|
||||||
cur = T
|
|
||||||
path = [T]
|
|
||||||
while V[cur][1] is not None:
|
|
||||||
path.append(V[cur][1])
|
|
||||||
cur = path[-1]
|
|
||||||
path.reverse()
|
|
||||||
return path
|
|
||||||
|
|
||||||
Readers should note that replacing ``Max`` in the above code with an
|
Readers should note that replacing ``Max`` in the above code with an
|
||||||
arbitrarily large number does not guarantee that the shortest path
|
arbitrarily large number does not guarantee that the shortest path
|
||||||
|
@ -222,6 +175,79 @@ graph, and set the 'arbitrarily large number' to that total. However,
|
||||||
doing so does not make the algorithm any easier to understand and has
|
doing so does not make the algorithm any easier to understand and has
|
||||||
potential problems with numeric overflows.
|
potential problems with numeric overflows.
|
||||||
|
|
||||||
|
.. _DijkstraSP_table_node:
|
||||||
|
|
||||||
|
Gustavo Niemeyer [9]_ points out that using a more Pythonic data
|
||||||
|
structure than tuples, to store information about node distances,
|
||||||
|
increases readability. Two equivalent node structures (one using
|
||||||
|
``None``, the other using ``Max``) and their use in a suitably
|
||||||
|
modified Dijkstra's shortest path algorithm is given below. ::
|
||||||
|
|
||||||
|
class SuperNode:
|
||||||
|
def __init__(self, node, parent, distance, visited):
|
||||||
|
self.node = node
|
||||||
|
self.parent = parent
|
||||||
|
self.distance = distance
|
||||||
|
self.visited = visited
|
||||||
|
|
||||||
|
class MaxNode(SuperNode):
|
||||||
|
def __init__(self, node, parent=None, distance=Max,
|
||||||
|
visited=False):
|
||||||
|
SuperNode.__init__(self, node, parent, distance, visited)
|
||||||
|
def __cmp__(self, other):
|
||||||
|
return cmp((self.visited, self.distance),
|
||||||
|
(other.visited, other.distance))
|
||||||
|
|
||||||
|
class NoneNode(SuperNode):
|
||||||
|
def __init__(self, node, parent=None, distance=None,
|
||||||
|
visited=False):
|
||||||
|
SuperNode.__init__(self, node, parent, distance, visited)
|
||||||
|
def __cmp__(self, other):
|
||||||
|
pair = ((self.visited, self.distance),
|
||||||
|
(other.visited, other.distance))
|
||||||
|
if None in (self.distance, other.distance):
|
||||||
|
return -cmp(*pair)
|
||||||
|
return cmp(*pair)
|
||||||
|
|
||||||
|
def DijkstraSP_table_node(graph, S, T, Node):
|
||||||
|
table = {} #3
|
||||||
|
for node in graph.iterkeys():
|
||||||
|
table[node] = Node(node) #1
|
||||||
|
table[S] = Node(S, distance=0) #2
|
||||||
|
cur = min(table.values()) #4a
|
||||||
|
sentinel = Node(None).distance
|
||||||
|
while not cur.visited and cur.distance != sentinel: #4
|
||||||
|
cur.visited = True #4b
|
||||||
|
for cdist, child in graph[node]: #4c
|
||||||
|
ndist = distance+cdist #|
|
||||||
|
if not table[child].visited and\ #|
|
||||||
|
ndist < table[child].distance: #|
|
||||||
|
table[child].distance = ndist #|_
|
||||||
|
cur = min(table.values()) #4a
|
||||||
|
if not table[T].visited:
|
||||||
|
return None
|
||||||
|
cur = T #5
|
||||||
|
path = [T] #|
|
||||||
|
while table[cur].parent is not None: #|
|
||||||
|
path.append(table[cur].parent) #|
|
||||||
|
cur = path[-1] #|
|
||||||
|
path.reverse() #|
|
||||||
|
return path #|_
|
||||||
|
|
||||||
|
In the above, passing in either NoneNode or MaxNode would be
|
||||||
|
sufficient to use either ``None`` or ``Max`` for the node distance
|
||||||
|
'infinity'. Note the additional special case required for ``None``
|
||||||
|
being used as a sentinel in NoneNode in the __cmp__ method.
|
||||||
|
|
||||||
|
This example highlights the special case handling where ``None`` is
|
||||||
|
used as a sentinel value for maximum values "in the wild", even though
|
||||||
|
None itself compares smaller than any other object in the standard
|
||||||
|
distribution.
|
||||||
|
|
||||||
|
As an aside, it is not clear to to the author that using Nodes as a
|
||||||
|
replacement for tuples has increased readability significantly, if at
|
||||||
|
all.
|
||||||
|
|
||||||
|
|
||||||
A ``Min`` Example
|
A ``Min`` Example
|
||||||
-----------------
|
-----------------
|
||||||
|
@ -241,33 +267,39 @@ following problem [6]_:
|
||||||
path.) Now suppose you are given two nodes in the graph, ``A``
|
path.) Now suppose you are given two nodes in the graph, ``A``
|
||||||
and ``B``.
|
and ``B``.
|
||||||
|
|
||||||
Such an algorithm is a 7 line modification to the DijkstraSP_table
|
Such an algorithm is a 7 line modification to the `DijkstraSP_table`_
|
||||||
algorithm given above::
|
algorithm given above (modified lines prefixed with `*`)::
|
||||||
|
|
||||||
#only showing the changed to lines with the proper indentation
|
def DijkstraSP_table(graph, S, T):
|
||||||
table[node] = (0, Min, node, None)
|
table = {} #3
|
||||||
table[S] = (0, 1, S, None)
|
for node in graph.iterkeys():
|
||||||
cur = max(table.values())
|
#(visited, distance, node, parent)
|
||||||
while (not cur[0]) and cur[1] > Min:
|
* table[node] = (0, Min, node, None) #1
|
||||||
ndist = distance*cdist
|
* table[S] = (0, 1, S, None) #2
|
||||||
if not table[child][0] and ndist > table[child][1]:
|
* cur = max(table.values()) #4a
|
||||||
cur = max(table.values())
|
* while (not cur[0]) and cur[1] > Min: #4
|
||||||
|
(visited, distance, node, parent) = cur
|
||||||
|
table[node] = (1, distance, node, parent) #4b
|
||||||
|
for cdist, child in graph[node]: #4c
|
||||||
|
* ndist = distance*cdist #|
|
||||||
|
* if not table[child][0] and ndist > table[child][1]:#|
|
||||||
|
table[child] = (0, ndist, child, node) #|_
|
||||||
|
* cur = max(table.values()) #4a
|
||||||
|
if not table[T][0]:
|
||||||
|
return None
|
||||||
|
cur = T #5
|
||||||
|
path = [T] #|
|
||||||
|
while table[cur][3] is not None: #|
|
||||||
|
path.append(table[cur][3]) #|
|
||||||
|
cur = path[-1] #|
|
||||||
|
path.reverse() #|
|
||||||
|
return path #|_
|
||||||
|
|
||||||
Or a 6 line modification to the DijkstraSP_heap algorithm given above
|
Note that there is a way of translating the graph to so that it can be
|
||||||
(if we assume that ``maxheapq`` exists and does what it is supposed
|
passed unchanged into the original `DijkstraSP_table`_ algorithm.
|
||||||
to)::
|
There also exists a handful of easy methods for constructing Node
|
||||||
|
objects that would work with `DijkstraSP_table_node`_. Such
|
||||||
#only showing the changed to lines with the proper indentation
|
translations are left as an exercise to the reader.
|
||||||
import maxheapq
|
|
||||||
Q = [(Min, i, None) for i in graph.iterkeys()]
|
|
||||||
maxheapq.heappush(Q, (1, S, None))
|
|
||||||
while Q[0][0] > Min and T not in V:
|
|
||||||
dist, node, parent = maxheapq.heappop(Q)
|
|
||||||
maxheapq.heappush(Q, (next*dist, dest, node))
|
|
||||||
|
|
||||||
Note that there is an equivalent way of translating the graph to
|
|
||||||
produce something that can be passed unchanged into the original
|
|
||||||
Dijkstra shortest path algorithm.
|
|
||||||
|
|
||||||
|
|
||||||
Other Examples
|
Other Examples
|
||||||
|
@ -333,6 +365,17 @@ seek to show how inconsistent they can be.
|
||||||
could result in incorrect output by passing in secondary versions of
|
could result in incorrect output by passing in secondary versions of
|
||||||
``Max``.
|
``Max``.
|
||||||
|
|
||||||
|
It has been pointed out [9]_ that the reference implementation given
|
||||||
|
below would be incompatible with independent implementations of
|
||||||
|
``Max``/``Min``. The point of this PEP is for the introduction of
|
||||||
|
"The One True Implementation" of "The One True Maximum" and "The One
|
||||||
|
True Minimum". User-based implementations of ``Max`` and ``Min``
|
||||||
|
objects would thusly be discouraged, and use of "The One True
|
||||||
|
Implementation" would obviously be encouraged. Ambiguous behavior
|
||||||
|
resulting from mixing users' implementations of ``Max`` and ``Min``
|
||||||
|
with "The One True Implementation" should be easy to discover through
|
||||||
|
variable and/or source code introspection.
|
||||||
|
|
||||||
|
|
||||||
Reference Implementation
|
Reference Implementation
|
||||||
========================
|
========================
|
||||||
|
@ -377,15 +420,14 @@ Results of Test Run::
|
||||||
Open Issues
|
Open Issues
|
||||||
===========
|
===========
|
||||||
|
|
||||||
Current options for the naming and namespace for ``Min``/``Max``, in
|
As the PEP was rejected, all open issues are now closed and
|
||||||
no particular order:
|
inconsequential. The module will use the names ``UniversalMaximum``
|
||||||
|
and ``UniversalMinimum`` due to the fact that it would be very
|
||||||
|
difficult to mistake what each does. For those who require a shorter
|
||||||
|
name, renaming the singletons during import is suggested::
|
||||||
|
|
||||||
1. Give the built-in ``max`` and ``min`` appropriate ``__cmp__``
|
from extremes import UniversalMaximum as uMax,
|
||||||
methods to allow them to double as ``Min``/``Max``.
|
UniversalMinimum as uMin
|
||||||
2. Attach them to attributes of the ``cmp()`` built-in.
|
|
||||||
3. Attach them to attributes of an appropriate type object.
|
|
||||||
4. Make them an appropriate module object.
|
|
||||||
5. Create two new built-ins with appropriate names.
|
|
||||||
|
|
||||||
|
|
||||||
References
|
References
|
||||||
|
@ -416,6 +458,28 @@ References
|
||||||
.. [8] Re: It's not really Some is it?, Ippolito, Bob
|
.. [8] Re: It's not really Some is it?, Ippolito, Bob
|
||||||
(http://www.livejournal.com/users/chouyu_31/138195.html?thread=274643#t274643)
|
(http://www.livejournal.com/users/chouyu_31/138195.html?thread=274643#t274643)
|
||||||
|
|
||||||
|
.. [9] [Python-Dev] Re: PEP 326 now online, Niemeyer, Gustavo
|
||||||
|
(http://mail.python.org/pipermail/python-dev/2004-January/042261.html);
|
||||||
|
[Python-Dev] Re: PEP 326 now online, Carlson, Josiah
|
||||||
|
(http://mail.python.org/pipermail/python-dev/2004-January/042272.html)
|
||||||
|
|
||||||
|
.. [10] PEP-0326 CVS, Carlson, Josiah
|
||||||
|
(http://cvs.sourceforge.net/viewcvs.py/python/python/nondist/peps/pep-0326.txt)
|
||||||
|
|
||||||
|
.. [11] [Python-Dev] PEP 326 (quick location possibility), Carlson, Josiah
|
||||||
|
(http://mail.python.org/pipermail/python-dev/2004-January/042275.html)
|
||||||
|
|
||||||
|
.. [12] [Python-Dev] PEP 326 (quick location possibility), van Rossum, Guido
|
||||||
|
(http://mail.python.org/pipermail/python-dev/2004-January/042306.html)
|
||||||
|
|
||||||
|
.. [13] [Python-Dev] PEP 326 (quick location possibility), Carlson, Josiah
|
||||||
|
(http://mail.python.org/pipermail/python-dev/2004-January/042300.html)
|
||||||
|
|
||||||
|
.. [14] Recommended standard implementation of PEP 326, extremes.py,
|
||||||
|
Carlson, Josiah
|
||||||
|
(http://www.ics.uci.edu/~jcarlson/pep326/extremes.py)
|
||||||
|
|
||||||
|
|
||||||
Changes
|
Changes
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
@ -425,8 +489,6 @@ Changes
|
||||||
|
|
||||||
- Changed markup to reStructuredText.
|
- Changed markup to reStructuredText.
|
||||||
|
|
||||||
- Concept gets a possible name and location. [5]_
|
|
||||||
|
|
||||||
- Clarified Abstract_, Motivation_, `Reference Implementation`_ and
|
- Clarified Abstract_, Motivation_, `Reference Implementation`_ and
|
||||||
`Open Issues`_ based on the simultaneous concepts of ``Max`` and
|
`Open Issues`_ based on the simultaneous concepts of ``Max`` and
|
||||||
``Min``.
|
``Min``.
|
||||||
|
@ -436,8 +498,6 @@ Changes
|
||||||
|
|
||||||
- Added an example of use for ``Min`` to Motivation_.
|
- Added an example of use for ``Min`` to Motivation_.
|
||||||
|
|
||||||
- Added some `Open Issues`_ and clarified some others.
|
|
||||||
|
|
||||||
- Added an example and `Other Examples`_ subheading.
|
- Added an example and `Other Examples`_ subheading.
|
||||||
|
|
||||||
- Modified `Reference Implementation`_ to instantiate both items from
|
- Modified `Reference Implementation`_ to instantiate both items from
|
||||||
|
@ -446,6 +506,13 @@ Changes
|
||||||
- Removed a large number of open issues that are not within the scope
|
- Removed a large number of open issues that are not within the scope
|
||||||
of this PEP.
|
of this PEP.
|
||||||
|
|
||||||
|
- Replaced an example from `Max Examples`_, changed an example in
|
||||||
|
`A Min Example`_.
|
||||||
|
|
||||||
|
- Added some `References`_.
|
||||||
|
|
||||||
|
- BDFL rejects [12]_ PEP 326
|
||||||
|
|
||||||
|
|
||||||
Copyright
|
Copyright
|
||||||
=========
|
=========
|
||||||
|
|
Loading…
Reference in New Issue