loop ($x = 1.0; $x <= 2.0; $x += (1/9))
, it's a crapshoot whether you get the "2.0" or not, and even if you do, it might actually be 1.99998 or something like that. Usually in these cases getting the exact ending value is highly desirable (as it is an edge case).(Before getting into potential solutions for this, let me note that this particular example is already solvable in the latest Rakudo builds using Rats.
loop ($x = 1; $x <= 2; $x += (1/9))
should give the exact list 1, 10/9, 11/9, 12/9, ... 17/9, 2. But that doesn't help with the fencepost, and the general problem is very ugly to solve this way!)My first thought was that
Range
with :by
might do the trick. Unfortunately, I don't see Perl 6 specs for how :by
actually works in this sort of case, and as far as I know, it's not actually implemented yet to test. At any rate, I'd like to suggest that :by
does actually handle this case, if possible (ie if an element of the range is within epsilon of the to
value, then the to
value is returned instead). And I'd like to suggest two more modifiers, something like :size(N)
to specify that the Range should return N values, with the :by
value calculated as appropriate, and :sections(N)
to specify that the Range should return N+1 values.In the meantime, here's a quick implementation of what I think the Range
:size
function should do. I believe that it is properly set up to be lazy, while laziness is available. Note that if you use integer parameters, it will return Rats!
I would have done it differently personally. I'd do something like, (1..9).map { $_* 1/9 + 1 }. That just seems to be a better soln.
ReplyDeleteHmmm... as you've written it, I don't know, but if you swap it around a little, you get (0..9).map({($_ / 9) * ($b - $a) + $a }). That's almost perfect, because the Rat version of 9 / 9 will be exactly equal to 1. But even then, the last number will be ($b - $a) + $a, and if those are floating point numbers, it is not guaranteed to be exactly equal to $a.
ReplyDelete