NAME
Sidef::Types::Range::Range - Range class for representing sequences of values
DESCRIPTION
The Range class implements a lazy sequence of values between two endpoints. A range does not necessarily store every value up front; many methods pull values one at a time from an internal iterator (self.iter), which makes ranges memory-efficient and lets them represent infinite sequences (e.g. 1..Inf).
That said, several methods documented below (anything that says "delegates to the array form") first materialize the entire range into a plain Array (via "to_a") before doing their work. Calling one of those methods, or any lazy method that never stops iterating (like "map" or "grep" with no early exit), on a genuinely infinite or unbounded range will not terminate. Methods that can stop early -- such as "first_by", "any", "contains", or a bounded call to "first" -- are safe to use on infinite ranges.
A range can be created using the range operator .. or by calling the Range constructor. Ranges are commonly used in loops, array slicing, and generating sequences.
SYNOPSIS
# Create a range from 1 to 10
var r = (1..10)
# Iterate over range
(1..10).each { |n|
say(n)
}
# Range with custom step
var evens = (0..20 `by` 2)
# Infinite ranges
var infinite = (1..Inf)
# Map over a range
var squares = (1..5).map { |n| n**2 }
# Filter range values
var odds = (1..20).grep { .is_odd }
# Shifting a range's bounds by a scalar
var r1 = (1..5)
say(r1 + 10) #=> a new range from 11 to 15
INHERITS
Inherits methods from:
* Sidef::Object::Object
CONSTRUCTION
new
Range.new(from, to, step)
Range(from, to, step)
Creates a new Range. This actually delegates directly to calling .range(to, step) on the given from value, so the real construction logic (including how a missing step is inferred from the direction of from and to) lives in from's own range method, not in this class.
var r1 = Range.new(1, 10)
var r2 = Range.new(0, 20, 2)
var r3 = Range(5, 1, -1)
Aliases: call
call
Range(from, to, step)
Allows Range to be invoked directly as a function, equivalent to "new".
Aliases: new
BOUNDS AND STEP
from
self.from
self.from(new_from)
With no argument, returns the range's start bound. With an argument, returns a new range with the start bound replaced by new_from, keeping the same end bound and step.
say((1..10).from) #=> 1
say((1..10).from(5)...) #=> [5, 6, 7, 8, 9, 10]
to
self.to
self.to(new_to)
With no argument, returns the range's end bound. With an argument, returns a new range with the end bound replaced by new_to, keeping the same start bound and step.
say((1..10).to) #=> 10
say((1..5).to(10)...) #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
by
self.by
self.by(step)
With no argument, returns the range's current step (same as "step"). With an argument, returns a new range using step as its step size, keeping the same bounds. If the range is currently descending (its existing step is negative), step is automatically negated first, so the range keeps counting down.
say((1..10 `by` 2)...) #=> [1, 3, 5, 7, 9]
say((0..20 `by` 5)...) #=> [0, 5, 10, 15, 20]
say((10..1 `by` 2)...) #=> [10, 8, 6, 4, 2] # 2 is negated internally to -2
step
self.step
Returns the step size of the range, as set at construction time (or inferred as 1 / -1 depending on direction).
say((1..10).step) #=> 1
say((0..20 `by` 5).step) #=> 5
say((10..1).step) #=> -1
min
self.min
self.min(block)
With no block, returns the numerically smaller of the range's two declared endpoints (not necessarily the last value actually produced by iteration, if step does not evenly divide the interval). With a block, delegates entirely to "min_by".
say((1..10).min) #=> 1
say((10..1).min) #=> 1
min_by
self.min_by(block)
Iterates the range lazily and returns the element for which block produces the smallest value (comparing results with the cmp operator). If block is omitted, elements are compared directly. On ties, the first element encountered is kept.
say((-5..5).min_by { |n| n.abs }) #=> 0
max
self.max
self.max(block)
With no block, returns the numerically larger of the range's two declared endpoints (subject to the same caveat as "min" about non-evenly-dividing steps). With a block, delegates entirely to "max_by".
say((1..10).max) #=> 10
say((10..1).max) #=> 10
max_by
self.max_by(block)
Iterates the range lazily and returns the element for which block produces the largest value. If block is omitted, elements are compared directly. On ties, the first element encountered is kept.
say((-5..5).max_by { |n| n.abs }) #=> 5
bounds
self.bounds
Returns a two-element list (min, max) -- always in ascending numeric order, regardless of whether the range counts up or down.
say((1..10).bounds) #=> (1, 10)
say((10..1).bounds) #=> (1, 10)
say((0..100 `by` 5).bounds) #=> (0, 100)
len
self.len
Returns the number of elements the range would generate, computed directly from its bounds and step (without iterating), as int((to - from + step) / step). Returns 0 if the computed length would be negative.
say((1..10).len) #=> 10
say((0..20 `by` 5).len) #=> 5
say((10..1).len) #=> 10
Aliases: length
reverse
self.reverse
Returns a new range with from and to swapped and the step negated, effectively reversing the direction of iteration.
say((1..10).reverse...) #=> [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
say((5..1).reverse...) #=> [1, 2, 3, 4, 5]
Aliases: flip
neg
self.neg
Returns a new range with only the from endpoint negated; to and step are left unchanged. This does not negate every element of the sequence -- only the starting bound.
say((1..5).neg...) #=> [-1, 0, 1, 2, 3, 4, 5] # from: 1 -> -1; to (5) and step (1) unchanged
say((-3..3).neg...) #=> [3] # from: -3 -> 3; to (3) and step (1) unchanged, so from == to
MEMBERSHIP AND COUNTING
contains
self.contains(value)
Checks whether value falls within the range's bounds and lies exactly on its step lattice (i.e., could actually be produced by iterating the range), taking direction into account.
say((1..10).contains(5)) #=> true
say((1..10).contains(15)) #=> false
say((1..10 `by` 2).contains(3)) #=> false
say((1..10 `by` 2).contains(5)) #=> true
Aliases: contain, include, includes
count
self.count(value)
self.count(block)
If given a plain value, returns 1 if the range "contains" it, or 0 otherwise (ranges never contain duplicate values, so this is a membership test rather than a general tally). If given a block, delegates to "count_by".
say((1..10).count(5)) #=> 1
say((1..10).count(15)) #=> 0
say((1..10).count { |n| n.is_even }) #=> 5
count_by
self.count_by(block)
Iterates the range lazily and returns the number of elements for which block returns a truthy value.
say((1..10).count_by { |n| n % 3 == 0 }) #=> 3
say((1..20).count_by { |n| n.is_prime }) #=> 8
ITERATION
each
self.each(block)
Iterates the range lazily, calling block once per element for side effects, and returns self.
(1..5).each { |n|
say(n)
}
# Prints: 1, 2, 3, 4, 5
Aliases: for, foreach
while
self.while(block)
Iterates the range lazily, calling block on each successive element, and stops as soon as block returns a falsy value (the element that caused the stop is consumed but not itself processed further). Unlike "grep", this does not collect any values -- it returns self.
(1..100).while { |n|
say(n)
n < 5
}
# Prints: 1, 2, 3, 4, 5, then stops (6 fails the condition and iteration ends)
each_cons
self.each_cons(n, block)
Iterates the range lazily over overlapping, consecutive windows of n elements, calling block with each window's elements as separate arguments (not as a single array). Returns self. If n is zero or negative, this is a no-op.
(1..10).each_cons(3, { |*group|
say(group)
})
# Prints: [1, 2, 3], [2, 3, 4], [3, 4, 5], ...
each_slice
self.each_slice(n, block)
Iterates the range lazily over non-overlapping slices of n elements, calling block with each slice's elements as separate arguments. The final, possibly shorter, slice is still passed to block. Returns self. If n is zero or negative, this is a no-op.
(1..10).each_slice(3, { |*slice|
say(slice)
})
# Prints: [1, 2, 3], [4, 5, 6], [7, 8, 9], [10]
SEARCHING
first
self.first
self.first(n)
self.first(block)
With no argument, returns the single first element of the range (or nil if empty). With a number n, returns an Array of the first n elements. With a block, delegates entirely to "first_by".
say((1..100).first) #=> 1
say((1..100).first(5)) #=> [1, 2, 3, 4, 5]
say((10..1).first(3)) #=> [10, 9, 8]
Aliases: head
first_by
self.first_by(block)
Iterates the range lazily and returns the first element for which block returns a truthy value, or nil if none does. Safe to use on infinite ranges, as long as a match exists.
say((1..100).first_by { |n| n > 50 && n.is_prime }) #=> 53
say((1..10).first_by { |n| n > 100 }) #=> nil
last
self.last
self.last(n)
self.last(block)
With no argument, returns the single last element of the range. With a number n, returns an Array of the last n elements, in their original order. With a block, returns the last element for which the block is truthy (equivalent to "last_by"). Implemented by reversing the range and searching from the other end, so it requires the range to be bounded.
say((1..10).last) #=> 10
say((1..10).last(3)) #=> [8, 9, 10]
Aliases: tail
last_by
self.last_by(block)
Returns the last element for which block returns a truthy value, or nil if none does. Implemented as self.reverse.first_by(block), so it requires the range to be bounded.
say((1..100).last_by { |n| n < 50 && n.is_prime }) #=> 47
say((1..10).last_by { |n| n > 100 }) #=> nil
PREDICATES
any
self.any(block)
Returns true as soon as block returns a truthy value for some element; returns false if the range is exhausted with no match. If block is omitted, elements are tested directly for truthiness.
say((1..10).any { |n| n > 5 }) #=> true
say((1..10).any { |n| n > 20 }) #=> false
all
self.all(block)
Returns false as soon as block returns a falsy value for some element; returns true if every element passes (including if the range is empty). If block is omitted, elements are tested directly for truthiness.
say((1..10).all { |n| n > 0 }) #=> true
say((1..10).all { |n| n.is_even }) #=> false
none
self.none(block)
Returns false as soon as block returns a truthy value for some element; returns true if no element matches. If block is omitted, elements are tested directly for truthiness.
say((1..10).none { |n| n > 20 }) #=> true
say((1..10).none { |n| n > 5 }) #=> false
TRANSFORMATION
map
self.map(block)
Iterates the range lazily, calling block once per element, and collects the results into a new Array. If block is omitted, the identity function is used (equivalent to "to_a").
say((1..5).map { |n| n**2 }) #=> [1, 4, 9, 16, 25]
say((1..3).map { |n| n * 10 }) #=> [10, 20, 30]
grep
self.grep(block)
Iterates the range lazily and collects into a new Array only the elements for which block returns a truthy value. If block is omitted, elements are kept based on their own truthiness.
say((1..20).grep { |n| n.is_prime }) #=> [2, 3, 5, 7, 11, 13, 17, 19]
say((1..10).grep { |n| n > 5 }) #=> [6, 7, 8, 9, 10]
Aliases: select
map_cons
self.map_cons(n, block)
Materializes the range into an Array (via "to_a") and delegates to that array's own map_cons method, which maps block over overlapping, consecutive groups of n elements and collects the results.
say((1..10).map_cons(3, { |*group| group.sum })) #=> [6, 9, 12, 15, 18, 21, 24, 27]
Aliases: cons
map_slice
self.map_slice(n, block)
Materializes the range into an Array (via "to_a") and delegates to that array's own map_slice method, which maps block over non-overlapping slices of n elements and collects the results.
say((1..10).map_slice(3, { |*slice| slice.sum }))
#=> [6, 15, 24, 10]
Aliases: slices
sort
self.sort(block)
Materializes the range into an Array (via "to_a") and delegates to that array's own sort method. With no block, sorts in natural order; with a block, uses it as the comparator.
say((1..10).sort { |a, b| b <=> a }) #=> [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
sort_by
self.sort_by(block)
Materializes the range into an Array (via "to_a") and delegates to that array's own sort_by method, sorting by the value block returns for each element.
say((-5..5).sort_by { |n| n.abs }) #=> [0, -1, 1, -2, 2, -3, 3, -4, 4, -5, 5]
accumulate
self.accumulate(block)
Materializes the range into an Array (via "to_a") and delegates to that array's own accumulate method, returning the running (cumulative) result of applying block pairwise across the elements.
say((1..5).accumulate { |a, b| a + b }) #=> [1, 3, 6, 10, 15]
say((1..4).accumulate { |a, b| a * b }) #=> [1, 2, 6, 24]
accumulate_by
self.accumulate_by(block)
Materializes the range into an Array (via "to_a") and delegates to that array's own accumulate_by method: applies block to each element first, then accumulates the results.
say((1..5).accumulate_by { |n| n**2 }) #=> [1, 5, 14, 30, 55]
REDUCTION
reduce
self.reduce(block)
self.reduce(block, initial)
self.reduce(op_name)
self.reduce(op_name, initial)
Reduces the range to a single value. If given a block, iterates lazily, using initial (if given) or the first element as the starting accumulator, then repeatedly calls block(accumulator, next_element). If given a string naming an operator (such as '+'), delegates to "reduce_operator" instead.
say((1..5).reduce { |a, b| a + b }) #=> 15
say((1..4).reduce { |a, b| a * b }) #=> 24
say((1..10).reduce('+')) #=> 55
say((1..5).reduce('+', 100)) #=> 115
reduce_operator
self.reduce_operator(op_name, initial=nil)
Reduces the range to a single value by repeatedly calling the named operator method (e.g. '+', '*') on an accumulator, starting from initial (or the first element, if omitted).
say((1..10).reduce_operator('+')) #=> 55
say((1..4).reduce_operator('*')) #=> 24
map_operator
self.map_operator(op_name, *args)
Materializes the range into an Array (via "to_a") and delegates to that array's own map_operator method, applying the named operator between each element and args.
say((1..5).map_operator('+', 10)) #=> [11, 12, 13, 14, 15]
say((1..5).map_operator('**', 2)) #=> [1, 4, 9, 16, 25]
pam_operator
self.pam_operator(op_name, *args)
Materializes the range into an Array (via "to_a") and delegates to that array's own pam_operator method. See the Array documentation for exact semantics.
say((1..5).pam_operator('+', 10))
unroll_operator
self.unroll_operator(op_name, *args)
Materializes the range into an Array (via "to_a") and delegates to that array's own unroll_operator method. See the Array documentation for exact semantics.
say((1..5).unroll_operator('+', 10))
cross_operator
self.cross_operator(op_name, *args)
Materializes the range into an Array (via "to_a") and delegates to that array's own cross_operator method, combining elements from self and args pairwise (cross-product style) using the named operator.
say((1..3).cross_operator('+', 10..12))
#=> [[11, 12, 13], [12, 13, 14], [13, 14, 15]]
zip_operator
self.zip_operator(op_name, *args)
Materializes the range into an Array (via "to_a") and delegates to that array's own zip_operator method, combining corresponding elements from self and args using the named operator.
say((1..3).zip_operator('+', 10..12)) #=> [11, 13, 15]
RANDOMIZATION
rand
self.rand
self.rand(n)
With no argument, returns a single random element from the range. With n, returns an Array of n random elements, drawn with replacement (duplicates possible). Computed directly from the bounds/step, without materializing the range.
say((1..10).rand) #=> a random number between 1 and 10
say((1..10).rand(5)) #=> 5 random numbers, possibly with duplicates
Aliases: sample
pick
self.pick
self.pick(n)
With no argument, returns a single random element from the range. With n, returns an Array of n distinct random elements (without replacement -- no duplicates), computed directly from the bounds/step.
say((1..10).pick) #=> a random number between 1 and 10
say((1..10).pick(3)) #=> 3 distinct random numbers, e.g. [7, 2, 9]
shuffle
self.shuffle
Materializes the range into a list and returns a new Array with its elements in random order.
say((1..10).shuffle) #=> a random permutation, e.g. [3, 7, 1, 9, 2, 5, 10, 4, 8, 6]
CONVERSION
to_array
self.to_array
Materializes the range by iterating it fully and returns the result as a new Array object.
say((1..5).to_array) #=> [1, 2, 3, 4, 5]
Aliases: to_a
to_list
self.to_list
Materializes the range by iterating it fully and returns its values as a plain list (not wrapped in an Array object, unlike "to_a").
var arr = (1..5)...
say(arr) #=> [1, 2, 3, 4, 5]
Aliases: operator ...
...
a...
Operator form of "to_list".
Aliases: to_list
to_vector
self.to_vector
Materializes the range (via "to_a") and converts it to a Vector object.
var vec = (1..5).to_vector
Aliases: to_v, to_vec
join
self.join(sep)
Materializes the range (via "to_list") and returns a String with its elements joined by sep.
say((1..5).join(', ')) #=> "1, 2, 3, 4, 5"
say((1..3).join(' + ')) #=> "1 + 2 + 3"
kv
self.kv
Materializes the range into an Array (via "to_a") and delegates to that array's own kv method, returning an array of [index, value] pairs.
say((10..13).kv) #=> [[0, 10], [1, 11], [2, 12], [3, 13]]
Aliases: pairs, zip_indices
ARITHMETIC
The operators below create a new range by applying the given operation to both of self's endpoints (the from and to values), leaving the step unchanged. arg is typically a plain number; since the operation is delegated directly to the endpoint values' own arithmetic methods, the result of using something other than a plain number as arg depends on how that value's own operator is implemented (outside the scope of this class).
add
self.add(arg)
self + arg
Returns a new range with arg added to both from and to.
say((1..5) + 10) #=> a range from 11 to 15
Aliases: operator +
+
self + arg
Operator form of "add".
Aliases: add
sub
self.sub(arg)
self - arg
Returns a new range with arg subtracted from both from and to.
say((10..15) - 5) #=> a range from 5 to 10
Aliases: operator -
-
self - arg
Operator form of "sub".
Aliases: sub
mul
self.mul(arg)
self * arg
Returns a new range with both from and to multiplied by arg.
say((1..5) * 2) #=> a range from 2 to 10, step 1
Aliases: operator *
*
self * arg
Operator form of "mul".
Aliases: mul
div
self.div(arg)
self / arg
Returns a new range with both from and to divided by arg.
say((10..20 `by` 5) / 2) #=> a range from 5 to 10, step 5
Aliases: operator /, operator ÷
/
self / arg
Operator form of "div".
Aliases: div
÷
self ÷ arg
Operator form of "div".
Aliases: div
COMPARISON
eq
self.eq(other)
self == other
Returns true if other is also a Range (of the exact same class) with the same from, to, and step; otherwise false.
say((1..10) == (1..10)) #=> true
say((1..10) == (1..20)) #=> false
say((1..10 `by` 2) == (1..10)) #=> false
Aliases: operator ==
==
self == other
Operator form of "eq".
Aliases: eq
ne
self.ne(other)
self != other
Returns the logical negation of "eq".
say((1..10) != (1..20)) #=> true
say((1..10) != (1..10)) #=> false
Aliases: operator !=, operator ≠
!=
self != other
Operator form of "ne".
Aliases: ne
≠
self ≠ other
Operator form of "ne".
Aliases: ne