On 9/27/2011 1:03 PM, Guido van Rossum wrote:

mimicking the behavior of range() for floats is a bad idea, and that
we need to come up with a name for an API that takes start/stop/count
arguments instead of start/stop/step.

[In the following, I use count as the number of intervals; the number of points is 1 more.]

I agree with others that we should not just have a floatrange. An exact-as-possible floatrange is trivially based on exact computations with fractions:

def floatrange(a, b, n):
    '''Yield floats a, b, and n-1 equally spaced floats in between.'''
for num,dem in fracrange(a.as_integer_ratio(), b.as_integer_ratio(), n):
        yield num/dem

There are good reasons to expose the latter. If fracrange is done with the Fraction class, each ratio will be reduced to lowest terms, which means that the denominator will vary for each pair. In some situations, one might prefer a constant denominator across the series. Once a constant denominator is calculated (eash though not trivial), fracrange is trivially based on range. The following makes the denominator as small as possible if the inputs are in lowest terms:

def fracrange(frac1, frac2, n):
'''Yield fractions frac1, frac2 and n-1 equally spaced fractions in between.
    Fractions are represented as (numerator, denominator > 0)  pairs.
    For output, use the smallest common denominator of the inputs
    that makes the numerator range an even multiple of n.
    '''
    n1, d1 = frac1
    n2, d2 = frac2
    dem = d1 * d2 // gcd(d1, d2)
    start = n1 * (dem // d1)
    stop = n2 * (dem // d2)
    rang = stop - start
    q, r = divmod(rang, n)
    if r:
        gcd_r_n = gcd(r, n)
        m =  n // gcd_r_n
        dem *= m
        start *= m
        stop *= m
        step  = rang // gcd_r_n  # rang * m // n
    else:
        step = q   # if r==0: gcd(r,n)==n, m==1, rang//n == q
    for num in range(start, stop+1, step):
        yield num,dem

Two example uses:
for i,j in fracrange((1,10), (22,10), 7): print(i,j)
print()
for i,j in fracrange((1,5), (1,1), 6): print(i,j)
## print

1 10
4 10
7 10
10 10
13 10
16 10
19 10
22 10

3 15
5 15
7 15
9 15
11 15
13 15
15 15

If nothing else, the above is easy to check for correctness ;-).
Note that for fraction output, one will normally want to be able to enter an explicit pair such as (1,5) or even (2,10) The decimal equivalent, .2, after conversion to float, gets converted by .as_integer_ratio() back to (3602879701896397, 18014398509481984).

--
Terry Jan Reedy

_______________________________________________
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com

Reply via email to