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$
|
||||
Author: Josiah Carlson <jcarlson@uci.edu>,
|
||||
Terry Reedy <tjreedy@udel.edu>
|
||||
Status: Draft
|
||||
Status: Rejected
|
||||
Type: Standards Track
|
||||
Content-Type: text/x-rst
|
||||
Created: 20-Dec-2003
|
||||
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
|
||||
|
@ -67,7 +79,7 @@ infinity). However, each has their drawbacks.
|
|||
File "<stdin>", line 1, in ?
|
||||
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
|
||||
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
|
||||
---------------------
|
||||
|
||||
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):
|
||||
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
|
||||
count = 5
|
||||
|
||||
::
|
||||
def counts(stop):
|
||||
i = 0
|
||||
while i < stop:
|
||||
yield i
|
||||
i += 1
|
||||
|
||||
def findmin_None(seq):
|
||||
cur = None
|
||||
for obj in seq:
|
||||
if obj < cur or (cur is None):
|
||||
cur = obj
|
||||
if cur is None:
|
||||
return cur
|
||||
return cur
|
||||
for client_number in counts(count):
|
||||
handle_one_client()
|
||||
|
||||
::
|
||||
|
||||
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)
|
||||
When using ``Max`` as the value assigned to count, our testing server
|
||||
becomes a production server with minimal effort.
|
||||
|
||||
As another example, in Dijkstra's shortest path algorithm on a graph
|
||||
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
|
||||
pointers to find the reverse of the path to be taken.
|
||||
|
||||
To be complete, below are two versions of the algorithm, one using a
|
||||
table (a bit more understandable) and one using a heap (much faster)::
|
||||
.. _DijkstraSP_table:
|
||||
|
||||
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):
|
||||
#runs in O(N^2) time using a table
|
||||
#find the shortest path
|
||||
table = {}
|
||||
table = {} #3
|
||||
for node in graph.iterkeys():
|
||||
#(visited, distance, node, parent)
|
||||
table[node] = (0, Max, node, None)
|
||||
table[S] = (0, 0, S, None)
|
||||
cur = min(table.values())
|
||||
while (not cur[0]) and cur[1] < Max:
|
||||
table[node] = (0, Max, node, None) #1
|
||||
table[S] = (0, 0, S, None) #2
|
||||
cur = min(table.values()) #4a
|
||||
while (not cur[0]) and cur[1] < Max: #4
|
||||
(visited, distance, node, parent) = cur
|
||||
table[node] = (1, distance, node, parent)
|
||||
for cdist, child in graph[node]:
|
||||
ndist = distance+cdist
|
||||
if not table[child][0] and ndist < table[child][1]:
|
||||
table[child] = (0, ndist, child, node)
|
||||
cur = min(table.values())
|
||||
#backtrace through results
|
||||
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 = min(table.values()) #4a
|
||||
if not table[T][0]:
|
||||
return None
|
||||
cur = T
|
||||
path = [T]
|
||||
while table[cur][3] is not None:
|
||||
path.append(table[cur][3])
|
||||
cur = path[-1]
|
||||
path.reverse()
|
||||
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
|
||||
cur = T #5
|
||||
path = [T] #|
|
||||
while table[cur][3] is not None: #|
|
||||
path.append(table[cur][3]) #|
|
||||
cur = path[-1] #|
|
||||
path.reverse() #|
|
||||
return path #|_
|
||||
|
||||
Readers should note that replacing ``Max`` in the above code with an
|
||||
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
|
||||
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
|
||||
-----------------
|
||||
|
@ -241,33 +267,39 @@ following problem [6]_:
|
|||
path.) Now suppose you are given two nodes in the graph, ``A``
|
||||
and ``B``.
|
||||
|
||||
Such an algorithm is a 7 line modification to the DijkstraSP_table
|
||||
algorithm given above::
|
||||
Such an algorithm is a 7 line modification to the `DijkstraSP_table`_
|
||||
algorithm given above (modified lines prefixed with `*`)::
|
||||
|
||||
#only showing the changed to lines with the proper indentation
|
||||
table[node] = (0, Min, node, None)
|
||||
table[S] = (0, 1, S, None)
|
||||
cur = max(table.values())
|
||||
while (not cur[0]) and cur[1] > Min:
|
||||
ndist = distance*cdist
|
||||
if not table[child][0] and ndist > table[child][1]:
|
||||
cur = max(table.values())
|
||||
def DijkstraSP_table(graph, S, T):
|
||||
table = {} #3
|
||||
for node in graph.iterkeys():
|
||||
#(visited, distance, node, parent)
|
||||
* table[node] = (0, Min, node, None) #1
|
||||
* table[S] = (0, 1, S, None) #2
|
||||
* cur = max(table.values()) #4a
|
||||
* 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
|
||||
(if we assume that ``maxheapq`` exists and does what it is supposed
|
||||
to)::
|
||||
|
||||
#only showing the changed to lines with the proper indentation
|
||||
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.
|
||||
Note that there is a way of translating the graph to so that it can be
|
||||
passed unchanged into the original `DijkstraSP_table`_ algorithm.
|
||||
There also exists a handful of easy methods for constructing Node
|
||||
objects that would work with `DijkstraSP_table_node`_. Such
|
||||
translations are left as an exercise to the reader.
|
||||
|
||||
|
||||
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
|
||||
``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
|
||||
========================
|
||||
|
@ -377,15 +420,14 @@ Results of Test Run::
|
|||
Open Issues
|
||||
===========
|
||||
|
||||
Current options for the naming and namespace for ``Min``/``Max``, in
|
||||
no particular order:
|
||||
As the PEP was rejected, all open issues are now closed and
|
||||
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__``
|
||||
methods to allow them to double as ``Min``/``Max``.
|
||||
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.
|
||||
from extremes import UniversalMaximum as uMax,
|
||||
UniversalMinimum as uMin
|
||||
|
||||
|
||||
References
|
||||
|
@ -416,6 +458,28 @@ References
|
|||
.. [8] Re: It's not really Some is it?, Ippolito, Bob
|
||||
(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
|
||||
=======
|
||||
|
||||
|
@ -425,8 +489,6 @@ Changes
|
|||
|
||||
- Changed markup to reStructuredText.
|
||||
|
||||
- Concept gets a possible name and location. [5]_
|
||||
|
||||
- Clarified Abstract_, Motivation_, `Reference Implementation`_ and
|
||||
`Open Issues`_ based on the simultaneous concepts of ``Max`` and
|
||||
``Min``.
|
||||
|
@ -436,8 +498,6 @@ Changes
|
|||
|
||||
- Added an example of use for ``Min`` to Motivation_.
|
||||
|
||||
- Added some `Open Issues`_ and clarified some others.
|
||||
|
||||
- Added an example and `Other Examples`_ subheading.
|
||||
|
||||
- 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
|
||||
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
|
||||
=========
|
||||
|
|
Loading…
Reference in New Issue