Skip to content

Commit d22c3a3

Browse files
authored
Make Flow.range support descending ranges with negative step (#455)
Fixes #439 — Flow.range with a negative step was only emitting the first element due to incorrect termination logic. The original implementation used a fixed termination condition (`t <= to`) that didn't account for negative steps. With a negative step, this condition would cause the loop to terminate prematurely. The fix updates the range method to: - Require non-zero step values - Use conditional termination logic: `t <= to` for positive steps, `t >= to` for negative steps - Document the descending range behavior with examples Test cases for descending ranges have been added to verify the fix works correctly. Closes #439.
1 parent f3d5930 commit d22c3a3

2 files changed

Lines changed: 16 additions & 7 deletions

File tree

core/src/main/scala/ox/flow/FlowCompanionOps.scala

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,18 @@ trait FlowCompanionOps:
8585
emit(t)
8686
t = f(t)
8787

88-
/** Creates a flow which emits a range of numbers, from `from`, to `to` (inclusive), stepped by `step`. */
89-
def range(from: Int, to: Int, step: Int): Flow[Int] = usingEmitInline: emit =>
90-
var t = from
91-
repeatWhile:
92-
emit(t)
93-
t = t + step
94-
t <= to
88+
/** Creates a flow which emits a range of numbers, from `from`, to `to` (inclusive), stepped by `step`. A negative `step` produces a
89+
* descending range, e.g. `range(5, 1, -1)` emits `5, 4, 3, 2, 1`. The `step` must be non-zero.
90+
*/
91+
def range(from: Int, to: Int, step: Int): Flow[Int] =
92+
require(step != 0, "step must be non-zero")
93+
usingEmitInline: emit =>
94+
var t = from
95+
repeatWhile:
96+
emit(t)
97+
t = t + step
98+
if step >= 0 then t <= to else t >= to
99+
end range
95100

96101
/** Creates a flow which emits the first element of tuples returned by repeated applications of `f`. The `initial` state is used for the
97102
* first application, and then the state is updated with the second element of the tuple. Emission stops when `f` returns `None`,

core/src/test/scala/ox/flow/FlowOpsFactoryMethodsTest.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,8 @@ class FlowOpsFactoryMethodsTest extends AnyFlatSpec with Matchers:
2525
Flow.range(1, 5, 1).runToList() shouldBe List(1, 2, 3, 4, 5)
2626
Flow.range(1, 5, 2).runToList() shouldBe List(1, 3, 5)
2727
Flow.range(1, 11, 3).runToList() shouldBe List(1, 4, 7, 10)
28+
29+
it should "produce a descending range with a negative step" in:
30+
Flow.range(5, 1, -1).runToList() shouldBe List(5, 4, 3, 2, 1)
31+
Flow.range(10, 0, -2).runToList() shouldBe List(10, 8, 6, 4, 2, 0)
2832
end FlowOpsFactoryMethodsTest

0 commit comments

Comments
 (0)