diff --git a/pep-0303.txt b/pep-0303.txt new file mode 100644 index 000000000..0856ac038 --- /dev/null +++ b/pep-0303.txt @@ -0,0 +1,181 @@ +PEP: 303 +Title: Extend divmod() for Multiple Divisors +Version: $Revision$ +Last-Modified: $Date$ +Author: Thomas Bellman +Status: Draft +Type: Standards Track +Content-Type: text/plain +Created: 31-Dec-2002 +Python-Version: 2.3 +Post-History: + + +Abstract + + This PEP describes an extension to the builtin divmod() function, + allowing it to take multiple divisors, chaining several calls to + divmod() into one. + + +Specification + + The builtin divmod() function would be changed to accept multiple + divisors, changing its signature from divmod(dividend, divisor) to + divmod(dividend, *divisors). The dividend is divided by the last + divisor, giving a quotient and a remainder. The quotient is then + divided by the second to last divisor, giving a new quotient and + remainder. This is repeated until all divisors have been used, + and divmod() then returns a tuple consisting of the quotient from + the last step, and the remainders from all the steps. + + A Python implementation of the new divmod() behaviour could look + like: + + def divmod(dividend, *divisors): + modulos = () + q = dividend + while divisors: + q,r = q.__divmod__(divisors[-1]) + modulos = (r,) + modulos + divisors = divisors[:-1] + return (q,) + modulos + + +Motivation + + Occasionally one wants to perform a chain of divmod() operations, + calling divmod() on the quotient from the previous step, with + varying divisors. The most common case is probably converting a + number of seconds into weeks, days, hours, minutes and seconds. + This would today be written as: + + def secs_to_wdhms(seconds): + m,s = divmod(seconds, 60) + h,m = divmod(m, 60) + d,h = divmod(h, 24) + w,d = divmod(d, 7) + return (w,d,h,m,s) + + This is tedious and easy to get wrong each time you need it. + + If instead the divmod() builtin is changed according the proposal, + the code for converting seconds to weeks, days, hours, minutes and + seconds then become + + def secs_to_wdhms(seconds): + w,d,h,m,s = divmod(seconds, 7, 24, 60, 60) + return (w,d,h,m,s) + + which is easier to type, easier to type correctly, and easier to + read. + + Other applications are: + + - Astronomical angles (declination is measured in degrees, minutes + and seconds, right ascension is measured in hours, minutes and + seconds). + - Old British currency (1 pound = 20 shilling, 1 shilling = 12 pence) + - Anglo-Saxon length units: 1 mile = 1760 yards, 1 yard = 3 feet, + 1 foot = 12 inches. + - Anglo-Saxon weight units: 1 long ton = 160 stone, 1 stone = 14 + pounds, 1 pound = 16 ounce, 1 ounce = 16 dram + - British volumes: 1 gallon = 4 quart, 1 quart = 2 pint, 1 pint + = 20 fluid ounces + + +Rationale + + The idea comes from APL, which has an operator that does this. (I + don't remember what the operator looks like, and it would probably + be impossible to render in ASCII anyway.) + + The APL operator takes a list as its second operand, while this + PEP proposes that each divisor should be a separate argument to + the divmod() function. This is mainly because it is expected that + the most common uses will have the divisors as constants right in + the call (as the 7, 24, 60, 60 above), and adding a set of + parentheses or brackets would just clutter the call. + + Requiring an explicit sequence as the second argument to divmod() + would seriously break backwards compatibility. Making divmod() + check its second argument for being a sequence is deemed to be too + ugly to contemplate. And in the case where one *does* have a + sequence that is computed other-where, it is easy enough to write + divmod(x, *divs) instead. + + Requiring at least one divisor, i.e rejecting divmod(x), has been + considered, but no good reason to do so has come to mind, and is + thus allowed in the name of generality. + + Calling divmod() with no divisors should still return a tuple (of + one element). Code that calls divmod() with a varying number of + divisors, and thus gets a return value with an "unknown" number of + elements, would otherwise have to special case that case. Code + that *knows* it is calling divmod() with no divisors is considered + to be too silly to warrant a special case. + + Processing the divisors in the other direction, i.e dividing with + the first divisor first, instead of dividing with the last divisor + first, has been considered. However, the result comes with the + most significant part first and the least significant part last + (think of the chained divmod as a way of splitting a number into + "digits", with varying weights), and it is reasonable to specify + the divisors (weights) in the same order as the result. + + The inverse operation: + + def inverse_divmod(seq, *factors): + product = seq[0] + for x,y in zip(factors, seq[1:]): + product = product * x + y + return product + + could also be useful. However, writing + + seconds = (((((w * 7) + d) * 24 + h) * 60 + m) * 60 + s) + + is less cumbersome both to write and to read than the chained + divmods. It is therefore deemed to be less important, and its + introduction can be deferred to its own PEP. Also, such a + function needs a good name, and the PEP author has not managed to + come up with one yet. + + Calling divmod("spam") does not raise an error, despite strings + supporting neither division nor modulo. However, unless we know + the other object too, we can't determine whether divmod() would + work or not, and thus it seems silly to forbid it. + + +Backwards Compatibility + + Any module that replaces the divmod() function in the __builtin__ + module, may cause other modules using the new syntax to break. It + is expected that this is very uncommon. + + Code that expects a TypeError exception when calling divmod() with + anything but two arguments will break. This is also expected to + be very uncommon. + + No other issues regarding backwards compatibility are known. + + +Reference Implementation + + Not finished yet, but it seems a rather straightforward + new implementation of the function builtin_divmod() in + Python/bltinmodule.c + + +Copyright + + This document has been placed in the public domain. + + + +Local Variables: +mode: indented-text +indent-tabs-mode: nil +sentence-end-double-space: t +fill-column: 70 +End: