breaking,fix: remove range exclusivity support

The usefulness of this feature is questionable, and its correct implementation
is difficult due to the use of generics. The implementation worked incorrectly
in cases where the iterator step is not equal to one and is not a multiple of
the end element. For example, for `range(0, 7, 4)`, the result is `[0]`
instead of `[0, 4]`. After this commit range end value is always included.

To check for multiples, a user-defined type must also implement an overload of
the remainder operator (`%`), even if the exclusivity function is not needed.

Another correct implementation requires subtracting one from the end element
for integers and the minimum fractional part supported by the type for floats.
This cannot be done correctly for generics, as it requires casting the literal
to a specific type, and we cannot cast number to any type.
This commit is contained in:
ge
2025-12-30 12:19:35 +03:00
parent bd3a692726
commit 4306b2220c
2 changed files with 5 additions and 42 deletions

View File

@@ -22,30 +22,18 @@ pub fn (mut r Range[T]) next() ?T {
return r.cur return r.cur
} }
@[params]
pub struct RangeConfig {
pub:
// If true exclude the end value from range.
exclusive bool
}
// range creates new Range iterator with given start, end and step values. // range creates new Range iterator with given start, end and step values.
// //
// Generally numbers are expected. If type is a struct the following operators // Generally numbers are expected. If type is a struct the following operators
// must be overloaded to perform comparisons and arithmetics: `+`, `-`, `<`, `==`. // must be overloaded to perform comparisons and arithmetics: `+`, `-`, `<`, `==`.
// See https://docs.vlang.io/limited-operator-overloading.html for details. // See https://docs.vlang.io/limited-operator-overloading.html for details.
// //
// By default, the range includes the end value. This behavior can be changed // The range includes the end value.
// by enabling the 'exclusive' option.
// //
// Note: Zero step value will cause an infitite loop! // Note: Zero step value will cause an infitite loop!
pub fn range[T](start T, end T, step T, config RangeConfig) Range[T] { pub fn range[T](start T, end T, step T) Range[T] {
mut limit := end
if config.exclusive {
limit -= step
}
return Range[T]{ return Range[T]{
limit: limit limit: end
step: step step: step
cur: start cur: start
is_neg: start > end is_neg: start > end
@@ -54,7 +42,6 @@ pub fn range[T](start T, end T, step T, config RangeConfig) Range[T] {
@[params] @[params]
pub struct RangeFromStringConfig { pub struct RangeFromStringConfig {
RangeConfig
pub: pub:
sep string = '-' sep string = '-'
group_sep string = ',' group_sep string = ','
@@ -92,7 +79,7 @@ pub fn from_string[T](s string, config RangeFromStringConfig) ![]Range[T] {
convert_string[T](range_str[0])!, convert_string[T](range_str[0])!,
convert_string[T](range_str[1])!, convert_string[T](range_str[1])!,
convert_string[T](range_str[2])!, convert_string[T](range_str[2])!,
config.RangeConfig) )
// vfmt on // vfmt on
} }
return result return result
@@ -133,7 +120,7 @@ pub fn from_string_custom[T](s string, conv StringConvertFn[T], config RangeFrom
start := conv[T](range_str[0])! start := conv[T](range_str[0])!
end := conv[T](range_str[1])! end := conv[T](range_str[1])!
step := conv[T](range_str[2])! step := conv[T](range_str[2])!
result << range(start, end, step, config.RangeConfig) result << range(start, end, step)
} }
return result return result
} }

View File

@@ -9,14 +9,6 @@ fn test_range() {
assert result == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] assert result == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
} }
fn test_range_exclusive() {
mut result := []int{}
for i in ranges.range[int](0, 10, 1, exclusive: true) {
result << i
}
assert result == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
}
fn test_range_negative() { fn test_range_negative() {
mut result := []int{} mut result := []int{}
for i in ranges.range[int](10, 0, -1) { for i in ranges.range[int](10, 0, -1) {
@@ -25,14 +17,6 @@ fn test_range_negative() {
assert result == [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] assert result == [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
} }
fn test_range_negative_exclusive() {
mut result := []int{}
for i in ranges.range[int](10, 0, -1, exclusive: true) {
result << i
}
assert result == [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
}
fn test_range_with_step() { fn test_range_with_step() {
mut result := []int{} mut result := []int{}
for i in ranges.range[int](0, 10, 2) { for i in ranges.range[int](0, 10, 2) {
@@ -65,14 +49,6 @@ fn test_range_single_item() {
assert result == [0] assert result == [0]
} }
fn test_range_single_item_exclusive() {
mut result := []int{}
for i in ranges.range(0, 1, 1, exclusive: true) {
result << i
}
assert result == [0]
}
fn test_range_bigint() { fn test_range_bigint() {
start := big.zero_int start := big.zero_int
end := big.integer_from_int(5) end := big.integer_from_int(5)