feat: add capacity(), is_valid(), remove is_empty(), add tests

This commit is contained in:
ge
2026-09-24 23:10:41 +03:00
parent d2c5ad5d2f
commit ceed23ceed
3 changed files with 55 additions and 24 deletions
+42 -21
View File
@@ -37,7 +37,15 @@ pub fn (r Range[T]) bounds() (T, T, T) {
return r.start, r.end, r.step return r.start, r.end, r.step
} }
// with_step returns copy of the range with new step value. // capacity returns the calculated number of elements in range.
pub fn (r Range[T]) capacity() int {
if !r.is_valid() {
return 0
}
return r.capacity_no_valid_check()
}
// with_step returns copy of the range with updated step value.
pub fn (r Range[T]) with_step[T](step T) Range[T] { pub fn (r Range[T]) with_step[T](step T) Range[T] {
return Range[T]{ return Range[T]{
...r ...r
@@ -46,20 +54,14 @@ pub fn (r Range[T]) with_step[T](step T) Range[T] {
} }
} }
// to_array returns an array of elements from the range. // to_array returns an array of elements from the range. This method is slightly
// faster than simply filling an array with elements in a loop, because the
// memory for the array is pre-allocated.
pub fn (r Range[T]) to_array() []T { pub fn (r Range[T]) to_array() []T {
if r.is_empty() { if !r.is_valid() {
return []T{} return []T{}
} }
cap := r.capacity_no_valid_check()
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} mut arr := []T{cap: cap}
for el in r { for el in r {
arr << el arr << el
@@ -67,10 +69,30 @@ pub fn (r Range[T]) to_array() []T {
return arr return arr
} }
// is_empty reports is the range instance empty (has no values). // is_valid reports is the range valid.
pub fn (r Range[T]) is_empty() bool { pub fn (r Range[T]) is_valid() bool {
empty := $zero(Range[T]) zero := T{}
return r == empty if r.step == zero {
return false
}
if r.step > zero && r.start > r.end {
return false
}
if r.step < zero && r.start <= r.end {
return false
}
return true
}
@[inline]
fn (r Range[T]) capacity_no_valid_check() int {
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()
}
return cap
} }
// range creates new Range iterator with given start, end and step values. // range creates new Range iterator with given start, end and step values.
@@ -81,7 +103,8 @@ pub fn (r Range[T]) is_empty() bool {
// //
// The range includes the end value. // The range includes the end value.
// //
// Note: If range cannot be created the empty range will be returned. See also `new()`. // Note: If range cannot be created the empty range will be returned.
// Is it recommended to check range with `is_valid()`. See also `new()`.
pub fn range[T](start T, end T, step T) Range[T] { pub fn range[T](start T, end T, step T) Range[T] {
return new(start, end, step) or { Range[T]{} } return new(start, end, step) or { Range[T]{} }
} }
@@ -200,8 +223,7 @@ pub fn from_string_custom[T](s string, conv StringConvertFn[T], config RangeFrom
fn split_string(s string, sep string) ![]string { fn split_string(s string, sep string) ![]string {
parts := s.split(sep) parts := s.split(sep)
if parts.any(|x| x.is_blank()) || parts.len !in [1, 2, 3] { if parts.any(|x| x.is_blank()) || parts.len !in [1, 2, 3] {
return error('`start${sep}end` or `start[:step]:end`' + return error('`start${sep}end` or `start[:step]:end`' + "formatted string expected, not '${s}'")
"formatted string expected, not '${s}'")
} }
if parts.len == 1 { if parts.len == 1 {
return [parts[0], parts[0], '1'] return [parts[0], parts[0], '1']
@@ -216,8 +238,7 @@ fn split_string(s string, sep string) ![]string {
if sep == ':' && parts.len == 3 { if sep == ':' && parts.len == 3 {
return [parts[0], parts[2], parts[1]] return [parts[0], parts[2], parts[1]]
} }
return error('invalid range string: expected `start[${sep}step]${sep}end` ' + return error('invalid range string: expected `start[${sep}step]${sep}end` ' + 'or `start${sep}end[/step]` format, got `${s}`')
'or `start${sep}end[/step]` format, got `${s}`')
} }
fn convert_string[T](s string) !T { fn convert_string[T](s string) !T {
+12 -2
View File
@@ -240,9 +240,9 @@ fn test_range_empty_bigint() {
assert r.to_array() == [] assert r.to_array() == []
} }
fn test_range_is_empty() { fn test_range_is_valid() {
r := ranges.range(0, 0, -9000) r := ranges.range(0, 0, -9000)
assert r.is_empty() assert !r.is_valid()
assert r.to_array() == [] assert r.to_array() == []
} }
@@ -306,3 +306,13 @@ fn test_new_error_negative_step_start_eq_end() {
assert err.msg() == 'step is negative, but start value is lesser than or equals end value' assert err.msg() == 'step is negative, but start value is lesser than or equals end value'
} }
} }
fn test_range_capacity() {
r := ranges.range(0, 9, 1)
assert r.capacity() == 10
}
fn test_range_capacity_non_one_step() {
r := ranges.range(0, 9, 2)
assert r.capacity() == 5
}
+1 -1
View File
@@ -1,7 +1,7 @@
Module { Module {
name: 'ranges' name: 'ranges'
description: 'Operating with ranges of numbers' description: 'Operating with ranges of numbers'
version: '0.6.0' version: '0.7.0'
license: 'Unlicense' license: 'Unlicense'
dependencies: [] dependencies: []
} }