diff --git a/ranges.v b/ranges.v index 8ec601d..ede596f 100644 --- a/ranges.v +++ b/ranges.v @@ -7,14 +7,17 @@ struct Range[T] { start T end T step T - is_neg bool // Is set to true if range end value is lesser than start value. + is_rev bool // Is set to true if range end value is lesser than start value. mut: cur T } // next returns the new element from range or none if range end is reached. pub fn (mut r Range[T]) next() ?T { - if (r.is_neg && r.cur < r.end) || (!r.is_neg && r.cur > r.end) { + if r.step == $zero(T) { + return none + } + if (r.is_rev && r.cur < r.end) || (!r.is_rev && r.cur > r.end) { return none } defer { @@ -43,6 +46,33 @@ pub fn (r Range[T]) with_step[T](step T) Range[T] { } } +// to_array returns an array of elements from the range. +pub fn (r Range[T]) to_array() []T { + if r.is_empty() { + return []T{} + } + + mut cap := 0 + + $if T is $int || T is $float { + cap = int((r.end - r.start) / r.step + T(1)) + } $else $if T is big.Integer { + cap = ((r.end - r.start) / r.step + big.one_int).int() + } + + mut arr := []T{cap: cap} + for el in r { + arr << el + } + return arr +} + +// is_empty reports is the range instance empty (has no values). +pub fn (r Range[T]) is_empty() bool { + empty := $zero(Range[T]) + return r == empty +} + // range creates new Range iterator with given start, end and step values. // // Generally numbers are expected. If type is a struct the following operators @@ -51,14 +81,34 @@ pub fn (r Range[T]) with_step[T](step T) Range[T] { // // The range includes the end value. // -// Note: Zero step value will cause an infitite loop! +// Note: If range cannot be created the empty range will be returned. See also `new()`. pub fn range[T](start T, end T, step T) Range[T] { + return new(start, end, step) or { Range[T]{} } +} + +// new creates new range with given start, end and step values. +// Unlike `range`, this function will return an error if: +// +// * step value is zero; +// * step > 0, but start value is greather than end value; +// * step < 0, but start value is lesser than end value. +pub fn new[T](start T, end T, step T) !Range[T] { + zero := $zero(T) + if step == zero { + return error('step value is zero') + } + if step > zero && start > end { + return error('step is positive, but start value is greather than end value') + } + if step < zero && start <= end { + return error('step is negative, but start value is lesser than or equals end value') + } return Range[T]{ start: start end: end step: step cur: start - is_neg: start > end + is_rev: start > end } } diff --git a/ranges_test.v b/ranges_test.v index 156f5f9..78df3b6 100644 --- a/ranges_test.v +++ b/ranges_test.v @@ -69,10 +69,15 @@ fn test_range_bigint() { fn test_range_from_string() { assert ranges.from_string[int]('0-10')! == [ranges.range(0, 10, 1)] - assert ranges.from_string[int]('0-7,8-15')! == [ranges.range(0, 7, 1), - ranges.range(8, 15, 1)] - assert ranges.from_string[int]('0-6,7,8-15')! == [ranges.range(0, 6, 1), - ranges.range(7, 7, 1), ranges.range(8, 15, 1)] + assert ranges.from_string[int]('0-7,8-15')! == [ + ranges.range(0, 7, 1), + ranges.range(8, 15, 1), + ] + assert ranges.from_string[int]('0-6,7,8-15')! == [ + ranges.range(0, 6, 1), + ranges.range(7, 7, 1), + ranges.range(8, 15, 1), + ] assert ranges.from_string[i64]('5:2:15', sep: ':')! == [ranges.range[i64](5, 15, 2)] assert ranges.from_string[int]('100:-1:0', sep: ':')! == [ ranges.range(100, 0, -1), @@ -193,3 +198,111 @@ fn test_range_bounds() { assert b == 10 assert c == 2 } + +fn test_range_to_array_int() { + r := ranges.range(0, 5, 1) + assert r.to_array() == [0, 1, 2, 3, 4, 5] +} + +fn test_range_to_array_float() { + r := ranges.range[f64](3.0, 3.14, 0.01) + assert r.to_array() == [ + f64(3.0), + 3.01, + 3.0199999999999996, + 3.0299999999999994, + 3.039999999999999, + 3.049999999999999, + 3.0599999999999987, + 3.0699999999999985, + 3.0799999999999983, + 3.089999999999998, + 3.099999999999998, + 3.1099999999999977, + 3.1199999999999974, + 3.1299999999999972, + 3.139999999999997, + ] +} + +fn test_range_to_array_bigint() { + r := ranges.range(big.zero_int, big.three_int, big.one_int) + assert r.to_array() == [big.zero_int, big.one_int, big.two_int, big.three_int] +} + +fn test_range_empty() { + r := ranges.range(0, 0, 0) + assert r.to_array() == [] +} + +fn test_range_empty_bigint() { + r := ranges.range(big.zero_int, big.zero_int, big.zero_int) + assert r.to_array() == [] +} + +fn test_range_is_empty() { + r := ranges.range(0, 0, -9000) + assert r.is_empty() + assert r.to_array() == [] +} + +fn test_new() { + r := ranges.new(0, 10, 1)! + a, b, c := r.bounds() + assert a == 0 + assert b == 10 + assert c == 1 + assert r.to_array() == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +} + +fn test_new_with_step() { + r := ranges.new(0, 10, 2)! + assert r.to_array() == [0, 2, 4, 6, 8, 10] +} + +fn test_new_reversed() { + r := ranges.new(10, 0, -1)! + assert r.to_array() == [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] +} + +fn test_new_with_negative_step() { + r := ranges.new(10, 0, -2)! + assert r.to_array() == [10, 8, 6, 4, 2, 0] +} + +fn test_new_single_item() { + r := ranges.new(5, 5, 1)! + assert r.to_array() == [5] +} + +fn test_new_error_zero_step() { + if _ := ranges.new(0, 10, 0) { + assert false, 'expected error for zero step' + } else { + assert err.msg() == 'step value is zero' + } +} + +fn test_new_error_positive_step_start_gt_end() { + if _ := ranges.new(10, 0, 1) { + assert false, 'expected error for positive step with start > end' + } else { + assert err.msg() == 'step is positive, but start value is greather than end value' + } +} + +fn test_new_error_negative_step_start_lt_end() { + if _ := ranges.new(0, 10, -1) { + assert false, 'expected error for negative step with start <= end' + } else { + assert err.msg() == 'step is negative, but start value is lesser than or equals end value' + } +} + +fn test_new_error_negative_step_start_eq_end() { + if _ := ranges.new(5, 5, -1) { + assert false, 'expected error for negative step with start <= end' + } else { + assert err.msg() == 'step is negative, but start value is lesser than or equals end value' + } +} diff --git a/v.mod b/v.mod index 75ddd1e..98f4da0 100644 --- a/v.mod +++ b/v.mod @@ -1,7 +1,7 @@ Module { name: 'ranges' description: 'Operating with ranges of numbers' - version: '0.5.0' + version: '0.6.0' license: 'Unlicense' dependencies: [] }