Hmm… that was all mildly confused and had some subtle bugs.
Here’s the mental model I think makes more sense:
0.0 to 3.1415 in 4
is equivalent to taking the integer range 0 to 4
and remapping it to an evenly spaced double range where 0 => 0.0 and 4 => 3.1415.
0.0 until 3.1415 in 4
is equivalent to taking the integer range 0 until 4
and remapping it to an evenly spaced double range where 0 => 0.0 and 4 => 3.1415, thereby omitting 3.1415 (since 4 gets omitted in the integer range).
a to b in 0
is ill-defined, you could make an argument for any of a
, (a+b)/2
, and b
, but I think a
is the right call.
a until b in 0
is empty.
a to|until b in k
for k>0
is well defined.
Corresponding code (hopefully without bugs ):
case class MyDoubleRange3(start: Double, end: Double, steps: Int, inclusive: Boolean) {
def foreach(f: Double => Unit): Unit = {
if (steps == 0) {
if (inclusive) f(start) // or f(end) or f((start+end)/2)
} else if (steps > 0) {
f(start)
for (i <- 1 until steps) f(start + ((end - start) / steps) * i)
if (inclusive) f(end)
}
}
}
We can bikeshed if the integer range should start at 0 or 1 - C vs Pascal anyone!?
I guess it depends on if you interpret k
as the “steps between the points” (= start at 0), or the “number of points I encounter in the foreach()” (= start at 1).
For the latter:
case class MyDoubleRange4(start: Double, end: Double, steps: Int, inclusive: Boolean) {
def foreach(f: Double => Unit): Unit = {
if (steps == 1) {
if (inclusive) f(start) // or f(end) or f((start+end)/2)
} else if (steps > 1) {
f(start)
for (i <- 1 until (steps - 1)) f(start + ((end - start) / (steps - 1)) * i)
if (inclusive) f(end)
}
}
}