class Range
A Range object represents an interval of values.
A range is useful for:
-
Testing whether a value falls within the interval.
-
Iterating over the values in the interval.
-
Slicing elements from a collection such as an array or a string.
Creating Ranges
You can create an Range object explicitly with:
-
A range literal:.
-
Method
Range.new.
Range Bounds
A range may have a lower bound, an upper bound, both, or neither.
The bounds for a range are set when the range is created, and are immutable:
-
The lower bound (which may be
nil) is its begin value, and is returned by methodbegin. -
The upper bound (which may be
nil) is its end value, and is returned by methodend.
A range may be:
-
Bounded: having definite begin and end values.
-
Beginless: having a
nilbegin value and a definite end value. -
Endless: having a definite begin value and a
nilend value. -
Unbounded: having
nilbegin and end values.
Bounded Ranges
A bounded range has definite values for both its begin and end values:
-
The two values for a range
rmust comparable; that is(r.begin <=> r.end) != nil. -
The begin value may be greater than the end value (but if so the range has no included values).
To create a bounded range with a Range literal, use the double-dot (included end) or triple-dot (excluded end) notation with definite begin and end values:
r = 0..4 # => 0..4 # Included end. r.include?(0) # => true r.include?(4) # => true # Included. r.include?(-1) # => false r.include?(5) # => false r.include?(:foo) # => false # Not comparable. r = 0...4 # => 0...4 # Exclude end. r.include?(0) # => true r.include?(3) # => true r.include?(-1) # => false r.include?(4) # => false # Excluded. r.include?(:foo) # => false # Not comparable.
To create a bounded range with Range.new, give definite values for both begin and end:
r = Range.new(0, 4) # => 0..4 # Include end. r.include?(4) # => true # Included. r = Range.new(0, 4, true) # => 0...4 # Exclude end. r.include?(4) # => false # Excluded.
Beginless Ranges
A beginless range has a nil begin value and a definite end value. Such a range includes all comparable values that are less than or equal to to the end value.
To create a beginless range with a Range literal, use the double-dot or triple-dot notation with a nil begin value and a definite end value:
r = nil..4 # => ..4 # Include end. r = ..4 # => ..4 # The leading nil may be omitted. r.include?(4) # => true # Included. r = nil...4 # => ...4 # Exclude end. r = ...4 # => ...4 # The leading nil may be omitted. r.include?(4) # => false # Excluded.
To create a beginless range with Range.new, give a nil begin value and a definite end value:
r = Range.new(nil, 4) # => ..4 # Include end. r.include?(4) # => true # Included. r = Range.new(nil, 4, true) # => ...4 # Exclude end. r.include?(4) # => false # Excluded.
Endless Ranges
An endless range has a definite begin value and a nil end value. Such a range includes all comparable values that are greater than or equal to to the begin value.
To create an endless range with a Range literal, use the double-dot or triple-dot notation with a definite begin value and a nil end value.
An endless range has no end value to exclude, so these ranges all have the same behavior:
0..nil # => 0.. 0.. # => 0.. # Trailing nil may be omitted. 0...nil # => 0... 0... # => 0... # Trailing nil may be omitted.
To create an endless range with Range.new, give a definite begin value and a nil end value.
As before, an endless range has no end value to exclude, so these ranges have the same behavior:
Range.new(0, nil) # => 0.. Range.new(0, nil, true) # => 0...
Unbounded Ranges
An unbounded range is both beginless and endless. Such a range includes all comparable values.
To create an unbounded range with a Range literal, give both begin and end values as nil.
An unbounded range has no end value to exclude, so these range have the same behavior;
nil..nil # => nil..nil ..nil # => nil..nil # The leading nil may be omitted. nil.. # => nil..nil # The trailing nil may be omitted. nil...nil # => nil...nil ...nil # => nil...nil # The leading nil may be omitted. nil... # => nil...nil # The trailing nil may be omitted.
To create an unbounded range with Range.new, give both begin and end values as nil.
As before, an unbounded range has no end value to exclude, so these range have the same behavior:
Range.new(nil, nil) # => nil..nil Range.new(nil, nil, true) # => nil...nil
Ranges and Other Classes
An object may be put into a range if its class implements instance method <=>. Ruby core classes that do so include Array, Complex, File::Stat, Float, Integer, Kernel, Module, Numeric, Rational, String, Symbol, and Time.
Example:
t0 = Time.now # => 2021-09-19 09:22:48.4854986 -0500 t1 = Time.now # => 2021-09-19 09:22:56.0365079 -0500 t2 = Time.now # => 2021-09-19 09:23:08.5263283 -0500 (t0..t2).include?(t1) # => true (t0..t1).include?(t2) # => false
A range can be iterated over only if its elements implement instance method succ. Ruby core classes that do so include Integer, String, and Symbol (but not the other classes mentioned above).
Iterator methods include:
-
Included from module Enumerable:
each_entry,each_with_index,each_with_object,each_slice,each_cons, andreverse_each.
Example:
a = [] (1..4).each {|i| a.push(i) } a # => [1, 2, 3, 4]
Ranges and User-Defined Classes
A user-defined class that is to be used in a range must implement instance method <=>; see Integer#<=>. To make iteration available, it must also implement instance method succ; see Integer#succ.
The class below implements both <=> and succ, and so can be used both to construct ranges and to iterate over them. Note that the Comparable module is included so the == method is defined in terms of <=>.
# Represent a string of 'X' characters. class Xs include Comparable attr_accessor :length def initialize(n) @length = n end def succ Xs.new(@length + 1) end def <=>(other) @length <=> other.length end def to_s sprintf "%2d #{inspect}", @length end def inspect 'X' * @length end end r = Xs.new(3)..Xs.new(6) #=> XXX..XXXXXX r.to_a #=> [XXX, XXXX, XXXXX, XXXXXX] r.include?(Xs.new(5)) #=> true r.include?(Xs.new(7)) #=> false
What’s Here
First, what’s elsewhere. Class Range:
-
Inherits from class Object.
-
Includes module Enumerable, which provides dozens of additional methods.
Here, class Range provides methods that are useful for:
Methods for Creating a Range
Methods for Querying
-
begin: Returns the begin value given forself. -
bsearch: Returns an element fromselfselected by a binary search. -
count: Returns a count of elements inself. -
end: Returns the end value given forself. -
exclude_end?: Returns whether the end object is excluded. -
first: Returns the first elements ofself. -
hash: Returns the integer hash code. -
last: Returns the last elements ofself. -
max: Returns the maximum values inself. -
min: Returns the minimum values inself. -
minmax: Returns the minimum and maximum values inself. -
size: Returns the count of elements inself.
Methods for Comparing
-
==: Returns whether a given object is equal toself(uses==). -
===: Returns whether the given object is between the begin and end values. -
cover?: Returns whether a given object is withinself. -
eql?: Returns whether a given object is equal toself(useseql?). -
include?(aliased asmember?): Returns whether a given object is an element ofself.
Methods for Iterating
-
%: Requires argumentn; calls the block with eachn-th element ofself. -
each: Calls the block with each element ofself. -
step: Takes optional argumentn(defaults to 1); calls the block with eachn-th element ofself.
Methods for Converting
-
inspect: Returns a string representation ofself(usesinspect). -
to_a(aliased asentries): Returns elements ofselfin an array.
Methods for Working with JSON
-
::json_create: Returns a new Range object constructed from the given object.
-
#as_json: Returns a 2-element hash representing
self. -
to_json: Returns a JSON string representingself.
To make these methods available:
require 'json/add/range'
Attributes
() → E
The object that defines the beginning of self.
(1..4).begin # => 1 (..2).begin # => nil
Related: Range#first, Range#end.
() → E
The object that defines the end of self.
(1..4).end # => 4 (1...4).end # => 4 (1..).end # => nil
Related: Range#begin, Range#last.
Public Class Methods
(E from, E to, ?boolish exclude_end) → void
Source
static VALUE
range_initialize(int argc, VALUE *argv, VALUE range)
{
VALUE beg, end, flags;
rb_scan_args(argc, argv, "21", &beg, &end, &flags);
range_modify(range);
range_init(range, beg, end, RBOOL(RTEST(flags)));
return Qnil;
}
Returns a new range based on the given objects begin and end. Optional argument exclude_end determines whether object end is included as the last object in the range:
Range.new(2, 5).to_a # => [2, 3, 4, 5] Range.new(2, 5, true).to_a # => [2, 3, 4] Range.new('a', 'd').to_a # => ["a", "b", "c", "d"] Range.new('a', 'd', true).to_a # => ["a", "b", "c"]
Public Instance Methods
(Numeric | int n) → Enumerator[E, self]
(Numeric | int n) { (E element) → void } → self
Source
static VALUE
range_percent_step(VALUE range, VALUE step)
{
return range_step(1, &step, range);
}
Same as step (but doesn’t provide default value for n). The method is convenient for experssive producing of Enumerator::ArithmeticSequence.
array = [0, 1, 2, 3, 4, 5, 6] # slice each second element: seq = (0..) % 2 #=> ((0..).%(2)) array[seq] #=> [0, 2, 4, 6] # or just array[(0..) % 2] #=> [0, 2, 4, 6]
Note that due to operator precedence in Ruby, parentheses are mandatory around range in this case:
(0..7) % 2 #=> ((0..7).%(2)) -- as expected 0..7 % 2 #=> 0..1 -- parsed as 0..(7 % 2)
(untyped obj) → bool
Source
static VALUE
range_eq(VALUE range, VALUE obj)
{
if (range == obj)
return Qtrue;
if (!rb_obj_is_kind_of(obj, rb_cRange))
return Qfalse;
return rb_exec_recursive_paired(recursive_equal, range, obj, obj);
}
Returns whether all of the following are true:
-
otheris a range. -
other.begin == self.begin. -
other.end == self.end. -
other.exclude_end? == self.exclude_end?.
Examples:
r = (1..5) r == (1..5) # => true r = Range.new(1, 5) r == 'foo' # => false r == (2..5) # => false r == (1..4) # => false r == (1...5) # => false r == Range.new(1, 5, true) # => false
Note that even with the same argument, the return values of == and eql? can differ:
(1..2) == (1..2.0) # => true (1..2).eql? (1..2.0) # => false
Related: Range#eql?.
(untyped obj) → bool
Source
static VALUE
range_eqq(VALUE range, VALUE val)
{
return r_cover_p(range, RANGE_BEG(range), RANGE_END(range), val);
}
Returns whether other is between self.begin and self.end:
(1..4) === 2 # => true (1..4) === 5 # => false (1..4) === 'a' # => false (1..4) === 4 # => true (1...4) === 4 # => false ('a'..'d') === 'c' # => true ('a'..'d') === 'e' # => false
A case statement uses method ===, and so:
case 79 when (1..50) "low" when (51..75) "medium" when (76..100) "high" end # => "high" case "2.6.5" when ..."2.4" "EOL" when "2.4"..."2.5" "maintenance" when "2.5"..."3.0" "stable" when "3.1".. "upcoming" end # => "stable"
() → ::Enumerator[E, E?]
() { (E) → (true | false) } → E?
() { (E) → ::Integer } → E?
Source
static VALUE
range_bsearch(VALUE range)
{
VALUE beg, end, satisfied = Qnil;
int smaller;
/* Implementation notes:
* Floats are handled by mapping them to 64 bits integers.
* Apart from sign issues, floats and their 64 bits integer have the
* same order, assuming they are represented as exponent followed
* by the mantissa. This is true with or without implicit bit.
*
* Finding the average of two ints needs to be careful about
* potential overflow (since float to long can use 64 bits).
*
* The half-open interval (low, high] indicates where the target is located.
* The loop continues until low and high are adjacent.
*
* -1/2 can be either 0 or -1 in C89. However, when low and high are not adjacent,
* the rounding direction of mid = (low + high) / 2 does not affect the result of
* the binary search.
*
* Note that -0.0 is mapped to the same int as 0.0 as we don't want
* (-1...0.0).bsearch to yield -0.0.
*/
#define BSEARCH(conv, excl) \
do { \
RETURN_ENUMERATOR(range, 0, 0); \
if (!(excl)) high++; \
low--; \
while (low + 1 < high) { \
mid = ((high < 0) == (low < 0)) ? low + ((high - low) / 2) \
: (low + high) / 2; \
BSEARCH_CHECK(conv(mid)); \
if (smaller) { \
high = mid; \
} \
else { \
low = mid; \
} \
} \
return satisfied; \
} while (0)
#define BSEARCH_FIXNUM(beg, end, excl) \
do { \
long low = FIX2LONG(beg); \
long high = FIX2LONG(end); \
long mid; \
BSEARCH(INT2FIX, (excl)); \
} while (0)
beg = RANGE_BEG(range);
end = RANGE_END(range);
if (FIXNUM_P(beg) && FIXNUM_P(end)) {
BSEARCH_FIXNUM(beg, end, EXCL(range));
}
#if SIZEOF_DOUBLE == 8 && defined(HAVE_INT64_T)
else if (RB_FLOAT_TYPE_P(beg) || RB_FLOAT_TYPE_P(end)) {
int64_t low = double_as_int64(NIL_P(beg) ? -HUGE_VAL : RFLOAT_VALUE(rb_Float(beg)));
int64_t high = double_as_int64(NIL_P(end) ? HUGE_VAL : RFLOAT_VALUE(rb_Float(end)));
int64_t mid;
BSEARCH(int64_as_double_to_num, EXCL(range));
}
#endif
else if (is_integer_p(beg) && is_integer_p(end)) {
RETURN_ENUMERATOR(range, 0, 0);
return bsearch_integer_range(beg, end, EXCL(range));
}
else if (is_integer_p(beg) && NIL_P(end)) {
VALUE diff = LONG2FIX(1);
RETURN_ENUMERATOR(range, 0, 0);
while (1) {
VALUE mid = rb_funcall(beg, '+', 1, diff);
BSEARCH_CHECK(mid);
if (smaller) {
if (FIXNUM_P(beg) && FIXNUM_P(mid)) {
BSEARCH_FIXNUM(beg, mid, false);
}
else {
return bsearch_integer_range(beg, mid, false);
}
}
diff = rb_funcall(diff, '*', 1, LONG2FIX(2));
beg = mid;
}
}
else if (NIL_P(beg) && is_integer_p(end)) {
VALUE diff = LONG2FIX(-1);
RETURN_ENUMERATOR(range, 0, 0);
while (1) {
VALUE mid = rb_funcall(end, '+', 1, diff);
BSEARCH_CHECK(mid);
if (!smaller) {
if (FIXNUM_P(mid) && FIXNUM_P(end)) {
BSEARCH_FIXNUM(mid, end, false);
}
else {
return bsearch_integer_range(mid, end, false);
}
}
diff = rb_funcall(diff, '*', 1, LONG2FIX(2));
end = mid;
}
}
else {
rb_raise(rb_eTypeError, "can't do binary search for %s", rb_obj_classname(beg));
}
return range;
}
Returns an element from self selected by a binary search.
See Binary Searching.
static VALUE
range_clamp(int argc, VALUE *argv, VALUE self)
{
VALUE self_beg = RANGE_BEG(self);
VALUE self_end = RANGE_END(self);
int self_excl = EXCL(self);
VALUE min, max;
int clamp_beg = 0, clamp_end = 0, excl = 0;
argc = rb_scan_args(argc, argv, "11", &min, &max);
if (argc == 1) {
VALUE range = min;
if (!rb_range_values(range, &min, &max, &excl)) {
rb_raise(rb_eTypeError, "wrong argument type %s (expected Range)",
rb_builtin_class_name(range));
}
}
if (!NIL_P(min) && !NIL_P(max) && r_cmp(min, max) > 0) {
rb_raise(rb_eArgError, "min argument must be less than or equal to max argument");
}
if (!NIL_P(min)) {
if (NIL_P(self_beg) || r_cmp(self_beg, min) < 0) {
clamp_beg = -1;
self_beg = min;
}
if (!NIL_P(self_end) && r_cmp(self_end, min) < 0) {
clamp_end = -1;
self_end = min;
}
}
if (!NIL_P(max)) {
if (clamp_beg == 0) {
if (!NIL_P(self_beg) && r_cmp(self_beg, max) > 0) {
clamp_beg = +1;
self_beg = max;
}
}
if (clamp_end == 0) {
int cmp = NIL_P(self_end) ? +1 : r_cmp(self_end, max);
if (cmp > 0) {
clamp_end = +1;
self_end = max;
self_excl = excl;
}
else if (cmp == 0) {
self_excl |= excl;
}
}
}
if (clamp_beg && clamp_beg == clamp_end) {
/* self is entirely outside the clamping bounds. */
self_excl = TRUE;
}
return rb_range_new(self_beg, self_end, self_excl);
}
Returns a new Range instance whose begin and end values are clamped to min and max, or to range.begin and range.end.
The returned range excludes its end if any of the following is true:
-
The returned end value is an excluded end value of
selfor range. -
Both begin and end values are clamped to the lower bound, or both are clamped to the upper bound. Since
selfis entirely outside the clamping bounds, the returned range is made empty by excluding its end.
Otherwise, the returned range includes its end.
Examples:
(1..10).clamp(3, 7) # => 3..7 (1...10).clamp(3, 7) # => 3..7 (1...10).clamp(3, 10) # => 3...10 (0...).clamp(0, 10) # => 0..10 (1..10).clamp(3..7) # => 3..7 (1..10).clamp(3...7) # => 3...7 (1..5).clamp(3...7) # => 3..5 (..10).clamp(3, 7) # => 3..7 (...10).clamp(3, 7) # => 3..7 (..10).clamp(3...7) # => 3...7 (..5).clamp(3...7) # => 3..5 (1..10).clamp(20..30) # => 20...20 (1..10).clamp(-10..0) # => 0...0 (..10).clamp(20..30) # => 20...20 (1..10).clamp(..7) # => 1..7 (1..10).clamp(...7) # => 1...7 (1..5).clamp(...7) # => 1..5 (1..10).clamp(3..) # => 3..10 (1..10).clamp(3...) # => 3..10 (1..).clamp(3..) # => 3.. (1...).clamp(3...) # => 3...
static VALUE
range_count(int argc, VALUE *argv, VALUE range)
{
if (argc != 0) {
/* It is odd for instance (1...).count(0) to return Infinity. Just let
* it loop. */
return rb_call_super(argc, argv);
}
else if (rb_block_given_p()) {
/* Likewise it is odd for instance (1...).count {|x| x == 0 } to return
* Infinity. Just let it loop. */
return rb_call_super(argc, argv);
}
VALUE beg = RANGE_BEG(range), end = RANGE_END(range);
if (NIL_P(beg) || NIL_P(end)) {
/* We are confident that the answer is Infinity. */
return DBL2NUM(HUGE_VAL);
}
if (is_integer_p(beg)) {
VALUE size = range_size(range);
if (!NIL_P(size)) {
return size;
}
}
return rb_call_super(argc, argv);
}
Returns the count of elements, based on an argument or block criterion, if given.
With no argument and no block given, returns the number of elements:
(1..4).count # => 4 (1...4).count # => 3 ('a'..'d').count # => 4 ('a'...'d').count # => 3 (1..).count # => Infinity (..4).count # => Infinity
With argument object, returns the number of object found in self, which will usually be zero or one:
(1..4).count(2) # => 1 (1..4).count(5) # => 0 (1..4).count('a') # => 0
With a block given, calls the block with each element; returns the number of elements for which the block returns a truthy value:
(1..4).count {|element| element < 3 } # => 2
Related: Range#size.
Source
static VALUE
range_cover(VALUE range, VALUE val)
{
VALUE beg, end;
beg = RANGE_BEG(range);
end = RANGE_END(range);
if (rb_obj_is_kind_of(val, rb_cRange)) {
return RBOOL(r_cover_range_p(range, beg, end, val));
}
return r_cover_p(range, beg, end, val);
}
Returns true if the given argument is within self, false otherwise.
With non-range argument object, evaluates with <= and <.
For range self with included end value (#exclude_end? == false), evaluates thus:
self.begin <= object <= self.end
Examples:
r = (1..4) r.cover?(1) # => true r.cover?(4) # => true r.cover?(0) # => false r.cover?(5) # => false r.cover?('foo') # => false r = ('a'..'d') r.cover?('a') # => true r.cover?('d') # => true r.cover?(' ') # => false r.cover?('e') # => false r.cover?(0) # => false
For range r with excluded end value (#exclude_end? == true), evaluates thus:
r.begin <= object < r.end
Examples:
r = (1...4) r.cover?(1) # => true r.cover?(3) # => true r.cover?(0) # => false r.cover?(4) # => false r.cover?('foo') # => false r = ('a'...'d') r.cover?('a') # => true r.cover?('c') # => true r.cover?(' ') # => false r.cover?('d') # => false r.cover?(0) # => false
With range argument range, compares the first and last elements of self and range:
r = (1..4) r.cover?(1..4) # => true r.cover?(0..4) # => false r.cover?(1..5) # => false r.cover?('a'..'d') # => false r = (1...4) r.cover?(1..3) # => true r.cover?(1..4) # => false
If begin and end are numeric, cover? behaves like include?
(1..3).cover?(1.5) # => true (1..3).include?(1.5) # => true
But when not numeric, the two methods may differ:
('a'..'d').cover?('cc') # => true ('a'..'d').include?('cc') # => false
Returns false if either:
-
The begin value of
selfis larger than its end value. -
An internal call to
<=>returnsnil; that is, the operands are not comparable.
Beginless ranges cover all values of the same type before the end, excluding the end for exclusive ranges. Beginless ranges cover ranges that end before the end of the beginless range, or at the end of the beginless range for inclusive ranges.
(..2).cover?(1) # => true (..2).cover?(2) # => true (..2).cover?(3) # => false (...2).cover?(2) # => false (..2).cover?("2") # => false (..2).cover?(..2) # => true (..2).cover?(...2) # => true (..2).cover?(.."2") # => false (...2).cover?(..2) # => false
Endless ranges cover all values of the same type after the beginning. Endless exclusive ranges do not cover endless inclusive ranges.
(2..).cover?(1) # => false (2..).cover?(3) # => true (2...).cover?(3) # => true (2..).cover?(2) # => true (2..).cover?("2") # => false (2..).cover?(2..) # => true (2..).cover?(2...) # => true (2..).cover?("2"..) # => false (2...).cover?(2..) # => false (2...).cover?(3...) # => true (2...).cover?(3..) # => false (3..).cover?(2..) # => false
Ranges that are both beginless and endless cover all values and ranges, and return true for all arguments, with the exception that beginless and endless exclusive ranges do not cover endless inclusive ranges.
(nil...).cover?(Object.new) # => true (nil...).cover?(nil...) # => true (nil..).cover?(nil...) # => true (nil...).cover?(nil..) # => false (nil...).cover?(1..) # => false
Related: Range#include?.
() { (E arg0) → untyped } → self
() → ::Enumerator[E, self]
Source
static VALUE
range_each(VALUE range)
{
VALUE beg, end;
long i;
RETURN_SIZED_ENUMERATOR(range, 0, 0, range_enum_size);
beg = RANGE_BEG(range);
end = RANGE_END(range);
if (FIXNUM_P(beg) && NIL_P(end)) {
range_each_fixnum_endless(beg);
}
else if (FIXNUM_P(beg) && FIXNUM_P(end)) { /* fixnums are special */
return range_each_fixnum_loop(beg, end, range);
}
else if (RB_INTEGER_TYPE_P(beg) && (NIL_P(end) || RB_INTEGER_TYPE_P(end))) {
if (SPECIAL_CONST_P(end) || RBIGNUM_POSITIVE_P(end)) { /* end >= FIXNUM_MIN */
if (!FIXNUM_P(beg)) {
if (RBIGNUM_NEGATIVE_P(beg)) {
do {
rb_yield(beg);
} while (!FIXNUM_P(beg = rb_big_plus(beg, INT2FIX(1))));
if (NIL_P(end)) range_each_fixnum_endless(beg);
if (FIXNUM_P(end)) return range_each_fixnum_loop(beg, end, range);
}
else {
if (NIL_P(end)) range_each_bignum_endless(beg);
if (FIXNUM_P(end)) return range;
}
}
if (FIXNUM_P(beg)) {
i = FIX2LONG(beg);
do {
rb_yield(LONG2FIX(i));
} while (POSFIXABLE(++i));
beg = LONG2NUM(i);
}
ASSUME(!FIXNUM_P(beg));
ASSUME(!SPECIAL_CONST_P(end));
}
if (!FIXNUM_P(beg) && RBIGNUM_SIGN(beg) == RBIGNUM_SIGN(end)) {
if (EXCL(range)) {
while (rb_big_cmp(beg, end) == INT2FIX(-1)) {
rb_yield(beg);
beg = rb_big_plus(beg, INT2FIX(1));
}
}
else {
VALUE c;
while ((c = rb_big_cmp(beg, end)) != INT2FIX(1)) {
rb_yield(beg);
if (c == INT2FIX(0)) break;
beg = rb_big_plus(beg, INT2FIX(1));
}
}
}
}
else if (SYMBOL_P(beg) && (NIL_P(end) || SYMBOL_P(end))) { /* symbols are special */
beg = rb_sym2str(beg);
if (NIL_P(end)) {
rb_str_upto_endless_each(beg, sym_each_i, 0);
}
else {
rb_str_upto_each(beg, rb_sym2str(end), EXCL(range), sym_each_i, 0);
}
}
else {
VALUE tmp = rb_check_string_type(beg);
if (!NIL_P(tmp)) {
if (!NIL_P(end)) {
rb_str_upto_each(tmp, end, EXCL(range), each_i, 0);
}
else {
rb_str_upto_endless_each(tmp, each_i, 0);
}
}
else {
if (!discrete_object_p(beg)) {
rb_raise(rb_eTypeError, "can't iterate from %s",
rb_obj_classname(beg));
}
if (!NIL_P(end))
range_each_func(range, each_i, 0);
else
for (;; beg = rb_funcallv(beg, id_succ, 0, 0))
rb_yield(beg);
}
}
return range;
}
With a block given, passes each element of self to the block:
a = [] (1..4).each {|element| a.push(element) } # => 1..4 a # => [1, 2, 3, 4]
Raises an exception unless self.first.respond_to?(:succ).
With no block given, returns an enumerator.
(untyped obj) → bool
Source
static VALUE
range_eql(VALUE range, VALUE obj)
{
if (range == obj)
return Qtrue;
if (!rb_obj_is_kind_of(obj, rb_cRange))
return Qfalse;
return rb_exec_recursive_paired(recursive_eql, range, obj, obj);
}
Returns true if and only if:
-
otheris a range. -
other.begin.eql?(self.begin). -
other.end.eql?(self.end). -
other.exclude_end? == self.exclude_end?.
Otherwise returns false.
r = (1..5) r.eql?(1..5) # => true r = Range.new(1, 5) r.eql?('foo') # => false r.eql?(2..5) # => false r.eql?(1..4) # => false r.eql?(1...5) # => false r.eql?(Range.new(1, 5, true)) # => false
Note that even with the same argument, the return values of == and eql? can differ:
(1..2) == (1..2.0) # => true (1..2).eql? (1..2.0) # => false
Related: Range#==.
() → bool
Source
static VALUE
range_exclude_end_p(VALUE range)
{
return RBOOL(EXCL(range));
}
Returns true if self excludes its end value; false otherwise:
Range.new(2, 5).exclude_end? # => false Range.new(2, 5, true).exclude_end? # => true (2..5).exclude_end? # => false (2...5).exclude_end? # => true
Source
static VALUE
range_first(int argc, VALUE *argv, VALUE range)
{
VALUE n, ary[2];
if (NIL_P(RANGE_BEG(range))) {
rb_raise(rb_eRangeError, "cannot get the first element of beginless range");
}
if (argc == 0) return RANGE_BEG(range);
rb_scan_args(argc, argv, "1", &n);
ary[0] = n;
ary[1] = rb_ary_new2(NUM2LEN(n));
rb_block_call(range, idEach, 0, 0, first_i, (VALUE)ary);
return ary[1];
}
With no argument, returns the first element of self, if it exists:
(1..4).first # => 1 ('a'..'d').first # => "a"
With non-negative integer argument n given, returns the first n elements in an array:
(1..10).first(3) # => [1, 2, 3] (1..10).first(0) # => [] (1..4).first(50) # => [1, 2, 3, 4]
Raises an exception if there is no first element:
(..4).first # Raises RangeError
() → Integer
Source
static VALUE
range_hash(VALUE range)
{
st_index_t hash = EXCL(range);
VALUE v;
hash = rb_hash_start(hash);
v = rb_hash(RANGE_BEG(range));
hash = rb_hash_uint(hash, NUM2LONG(v));
v = rb_hash(RANGE_END(range));
hash = rb_hash_uint(hash, NUM2LONG(v));
hash = rb_hash_uint(hash, EXCL(range) << 24);
hash = rb_hash_end(hash);
return ST2FIX(hash);
}
Returns the integer hash value for self. Two range objects r0 and r1 have the same hash value if and only if r0.eql?(r1).
Related: Range#eql?, Object#hash.
(untyped obj) → bool
Returns true if object is an element of self, false otherwise:
(1..4).include?(2) # => true (1..4).include?(5) # => false (1..4).include?(4) # => true (1...4).include?(4) # => false ('a'..'d').include?('b') # => true ('a'..'d').include?('e') # => false ('a'..'d').include?('B') # => false ('a'..'d').include?('d') # => true ('a'...'d').include?('d') # => false
If begin and end are numeric, include? behaves like cover?
(1..3).include?(1.5) # => true (1..3).cover?(1.5) # => true
But when not numeric, the two methods may differ:
('a'..'d').include?('cc') # => false ('a'..'d').cover?('cc') # => true
Related: Range#cover?.
() → String
Source
static VALUE
range_inspect(VALUE range)
{
return rb_exec_recursive(inspect_range, range, 0);
}
Returns a string representation of self, including begin.inspect and end.inspect:
(1..4).inspect # => "1..4" (1...4).inspect # => "1...4" (1..).inspect # => "1.." (..4).inspect # => "..4"
Note that returns from to_s and inspect may differ:
('a'..'d').to_s # => "a..d" ('a'..'d').inspect # => "\"a\"..\"d\""
Related: Range#to_s.
Source
static VALUE
range_last(int argc, VALUE *argv, VALUE range)
{
if (NIL_P(RANGE_END(range))) {
rb_raise(rb_eRangeError, "cannot get the last element of endless range");
}
if (argc == 0) return RANGE_END(range);
if (integer_end_optimizable(range)) {
return rb_int_range_last(argc, argv, range);
}
return rb_ary_last(argc, argv, rb_Array(range));
}
With no argument, returns the last element of self, if it exists:
(1..4).last # => 4 ('a'..'d').last # => "d"
Note that last with no argument returns the end element of self even if exclude_end? is true:
(1...4).last # => 4 ('a'...'d').last # => "d"
With non-negative integer argument n given, returns the last n elements in an array:
(1..10).last(3) # => [8, 9, 10] (1..10).last(0) # => [] (1..4).last(50) # => [1, 2, 3, 4]
Note that last with argument does not return the end element of self if exclude_end? it true:
(1...4).last(3) # => [1, 2, 3] ('a'...'d').last(3) # => ["a", "b", "c"]
Raises an exception if there is no last element:
(1..).last # Raises RangeError
static VALUE
range_max(int argc, VALUE *argv, VALUE range)
{
VALUE e = RANGE_END(range);
int nm = FIXNUM_P(e) || rb_obj_is_kind_of(e, rb_cNumeric);
if (NIL_P(RANGE_END(range))) {
rb_raise(rb_eRangeError, "cannot get the maximum of endless range");
}
VALUE b = RANGE_BEG(range);
if (rb_block_given_p() || (EXCL(range) && !nm)) {
if (NIL_P(b)) {
rb_raise(rb_eRangeError, "cannot get the maximum of beginless range with custom comparison method");
}
return rb_call_super(argc, argv);
}
else if (argc) {
VALUE ary[2];
ID reverse_each;
CONST_ID(reverse_each, "reverse_each");
rb_scan_args(argc, argv, "1", &ary[0]);
ary[1] = rb_ary_new2(NUM2LEN(ary[0]));
rb_block_call(range, reverse_each, 0, 0, first_i, (VALUE)ary);
return ary[1];
#if 0
if (integer_end_optimizable(range)) {
return rb_int_range_last(argc, argv, range, true);
}
return rb_ary_reverse(rb_ary_last(argc, argv, rb_Array(range)));
#endif
}
else {
int c = NIL_P(b) ? -1 : OPTIMIZED_CMP(b, e);
if (c > 0)
return Qnil;
if (EXCL(range)) {
if (!RB_INTEGER_TYPE_P(e)) {
rb_raise(rb_eTypeError, "cannot exclude non Integer end value");
}
if (c == 0) return Qnil;
if (!NIL_P(b) && !RB_INTEGER_TYPE_P(b)) {
rb_raise(rb_eTypeError, "cannot exclude end value with non Integer begin value");
}
if (FIXNUM_P(e)) {
return LONG2NUM(FIX2LONG(e) - 1);
}
return rb_int_minus(e,INT2FIX(1));
}
return e;
}
}
Returns the maximum value in self, using method <=> or a given block for comparison.
With no argument and no block given, returns the maximum-valued element of self.
(1..4).max # => 4 ('a'..'d').max # => "d" (-4..-1).max # => -1
With non-negative integer argument n given, and no block given, returns the n maximum-valued elements of self in an array:
(1..4).max(2) # => [4, 3] ('a'..'d').max(2) # => ["d", "c"] (-4..-1).max(2) # => [-1, -2] (1..4).max(50) # => [4, 3, 2, 1]
If a block is given, it is called:
-
First, with the first two element of
self. -
Then, sequentially, with the so-far maximum value and the next element of
self.
To illustrate:
(1..4).max {|a, b| p [a, b]; a <=> b } # => 4
Output:
[2, 1] [3, 2] [4, 3]
With no argument and a block given, returns the return value of the last call to the block:
(1..4).max {|a, b| -(a <=> b) } # => 1
With non-negative integer argument n given, and a block given, returns the return values of the last n calls to the block in an array:
(1..4).max(2) {|a, b| -(a <=> b) } # => [1, 2] (1..4).max(50) {|a, b| -(a <=> b) } # => [1, 2, 3, 4]
Returns an empty array if n is zero:
(1..4).max(0) # => [] (1..4).max(0) {|a, b| -(a <=> b) } # => []
Returns nil or an empty array if:
-
The begin value of the range is larger than the end value:
(4..1).max # => nil (4..1).max(2) # => [] (4..1).max {|a, b| -(a <=> b) } # => nil (4..1).max(2) {|a, b| -(a <=> b) } # => []
-
The begin value of an exclusive range is equal to the end value:
(1...1).max # => nil (1...1).max(2) # => [] (1...1).max {|a, b| -(a <=> b) } # => nil (1...1).max(2) {|a, b| -(a <=> b) } # => []
Raises an exception if either:
-
selfis a endless range:(1..). -
A block is given and
selfis a beginless range.
Related: Range#min, Range#minmax.
(untyped obj) → bool
Source
static VALUE
range_include(VALUE range, VALUE val)
{
VALUE ret = range_include_internal(range, val);
if (!UNDEF_P(ret)) return ret;
return rb_call_super(1, &val);
}
static VALUE
range_min(int argc, VALUE *argv, VALUE range)
{
if (NIL_P(RANGE_BEG(range))) {
rb_raise(rb_eRangeError, "cannot get the minimum of beginless range");
}
if (rb_block_given_p()) {
if (NIL_P(RANGE_END(range))) {
rb_raise(rb_eRangeError, "cannot get the minimum of endless range with custom comparison method");
}
return rb_call_super(argc, argv);
}
else if (argc != 0) {
return range_first(argc, argv, range);
}
else {
VALUE b = RANGE_BEG(range);
VALUE e = RANGE_END(range);
int c = NIL_P(e) ? -1 : OPTIMIZED_CMP(b, e);
if (c > 0 || (c == 0 && EXCL(range)))
return Qnil;
return b;
}
}
Returns the minimum value in self, using method <=> or a given block for comparison.
With no argument and no block given, returns the minimum-valued element of self.
(1..4).min # => 1 ('a'..'d').min # => "a" (-4..-1).min # => -4
With non-negative integer argument n given, and no block given, returns the n minimum-valued elements of self in an array:
(1..4).min(2) # => [1, 2] ('a'..'d').min(2) # => ["a", "b"] (-4..-1).min(2) # => [-4, -3] (1..4).min(50) # => [1, 2, 3, 4]
If a block is given, it is called:
-
First, with the first two element of
self. -
Then, sequentially, with the so-far minimum value and the next element of
self.
To illustrate:
(1..4).min {|a, b| p [a, b]; a <=> b } # => 1
Output:
[2, 1] [3, 1] [4, 1]
With no argument and a block given, returns the return value of the last call to the block:
(1..4).min {|a, b| -(a <=> b) } # => 4
With non-negative integer argument n given, and a block given, returns the return values of the last n calls to the block in an array:
(1..4).min(2) {|a, b| -(a <=> b) } # => [4, 3] (1..4).min(50) {|a, b| -(a <=> b) } # => [4, 3, 2, 1]
Returns an empty array if n is zero:
(1..4).min(0) # => [] (1..4).min(0) {|a, b| -(a <=> b) } # => []
Returns nil or an empty array if:
-
The begin value of the range is larger than the end value:
(4..1).min # => nil (4..1).min(2) # => [] (4..1).min {|a, b| -(a <=> b) } # => nil (4..1).min(2) {|a, b| -(a <=> b) } # => []
-
The begin value of an exclusive range is equal to the end value:
(1...1).min # => nil (1...1).min(2) # => [] (1...1).min {|a, b| -(a <=> b) } # => nil (1...1).min(2) {|a, b| -(a <=> b) } # => []
Raises an exception if either:
-
selfis a beginless range:(..4). -
A block is given and
selfis an endless range.
Related: Range#max, Range#minmax.
static VALUE
range_minmax(VALUE range)
{
if (rb_block_given_p()) {
return rb_call_super(0, NULL);
}
return rb_assoc_new(
rb_funcall(range, id_min, 0),
rb_funcall(range, id_max, 0)
);
}
Returns a 2-element array containing the minimum and maximum value in self, either according to comparison method <=> or a given block.
With no block given, returns the minimum and maximum values, using <=> for comparison:
(1..4).minmax # => [1, 4] (1...4).minmax # => [1, 3] ('a'..'d').minmax # => ["a", "d"] (-4..-1).minmax # => [-4, -1]
With a block given, the block must return an integer:
-
Negative if
ais smaller thanb. -
Zero if
aandbare equal. -
Positive if
ais larger thanb.
The block is called self.size times to compare elements; returns a 2-element Array containing the minimum and maximum values from self, per the block:
(1..4).minmax {|a, b| -(a <=> b) } # => [4, 1]
Returns [nil, nil] if:
-
The begin value of the range is larger than the end value:
(4..1).minmax # => [nil, nil] (4..1).minmax {|a, b| -(a <=> b) } # => [nil, nil]
-
The begin value of an exclusive range is equal to the end value:
(1...1).minmax # => [nil, nil] (1...1).minmax {|a, b| -(a <=> b) } # => [nil, nil]
Raises an exception if self is a beginless or an endless range.
(Range[untyped]) → bool
Source
static VALUE
range_overlap(VALUE range, VALUE other)
{
if (!rb_obj_is_kind_of(other, rb_cRange)) {
rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Range)",
rb_class_name(rb_obj_class(other)));
}
VALUE self_beg = RANGE_BEG(range);
VALUE self_end = RANGE_END(range);
int self_excl = EXCL(range);
VALUE other_beg = RANGE_BEG(other);
VALUE other_end = RANGE_END(other);
int other_excl = EXCL(other);
if (empty_region_p(self_beg, other_end, other_excl)) return Qfalse;
if (empty_region_p(other_beg, self_end, self_excl)) return Qfalse;
if (!NIL_P(self_beg) && !NIL_P(other_beg)) {
VALUE cmp = rb_funcall(self_beg, id_cmp, 1, other_beg);
if (NIL_P(cmp)) return Qfalse;
/* if both begin values are equal, no more comparisons needed */
if (rb_cmpint(cmp, self_beg, other_beg) == 0) return Qtrue;
}
else if (NIL_P(self_beg) && !NIL_P(self_end) && NIL_P(other_beg) && !NIL_P(other_end)) {
VALUE cmp = rb_funcall(self_end, id_cmp, 1, other_end);
return RBOOL(!NIL_P(cmp));
}
if (empty_region_p(self_beg, self_end, self_excl)) return Qfalse;
if (empty_region_p(other_beg, other_end, other_excl)) return Qfalse;
return Qtrue;
}
Returns true if range overlaps with self, false otherwise:
(0..2).overlap?(1..3) #=> true (0..2).overlap?(3..4) #=> false (0..).overlap?(..0) #=> true
With non-range argument, raises TypeError.
(1..3).overlap?(1) # TypeError
Returns false if an internal call to <=> returns nil; that is, the operands are not comparable.
(1..3).overlap?('a'..'d') # => false
Returns false if self or range is empty. “Empty range” means that its begin value is larger than, or equal for an exclusive range, its end value.
(4..1).overlap?(2..3) # => false (4..1).overlap?(..3) # => false (4..1).overlap?(2..) # => false (2...2).overlap?(1..2) # => false (1..4).overlap?(3..2) # => false (..4).overlap?(3..2) # => false (1..).overlap?(3..2) # => false (1..2).overlap?(2...2) # => false
Returns false if the begin value one of self and range is larger than, or equal if the other is an exclusive range, the end value of the other:
(4..5).overlap?(2..3) # => false (4..5).overlap?(2...4) # => false (1..2).overlap?(3..4) # => false (1...3).overlap?(3..4) # => false
Returns false if the end value one of self and range is larger than, or equal for an exclusive range, the end value of the other:
(4..5).overlap?(2..3) # => false (4..5).overlap?(2...4) # => false (1..2).overlap?(3..4) # => false (1...3).overlap?(3..4) # => false
Note that the method wouldn’t make any assumptions about the beginless range being actually empty, even if its upper bound is the minimum possible value of its type, so all this would return true:
(...-Float::INFINITY).overlap?(...-Float::INFINITY) # => true (..."").overlap?(..."") # => true (...[]).overlap?(...[]) # => true
Even if those ranges are effectively empty (no number can be smaller than -Float::INFINITY), they are still considered overlapping with themselves.
Related: Range#cover?.
() { (E) → void } → self
() → ::Enumerator[E, self]
Source
static VALUE
range_reverse_each(VALUE range)
{
RETURN_SIZED_ENUMERATOR(range, 0, 0, range_enum_reverse_size);
VALUE beg = RANGE_BEG(range);
VALUE end = RANGE_END(range);
int excl = EXCL(range);
if (NIL_P(end)) {
rb_raise(rb_eTypeError, "can't iterate from %s",
rb_obj_classname(end));
}
if (FIXNUM_P(beg) && FIXNUM_P(end)) {
if (excl) {
if (end == LONG2FIX(FIXNUM_MIN)) return range;
end = rb_int_minus(end, INT2FIX(1));
}
range_reverse_each_fixnum_section(beg, end);
}
else if ((NIL_P(beg) || RB_INTEGER_TYPE_P(beg)) && RB_INTEGER_TYPE_P(end)) {
if (excl) {
end = rb_int_minus(end, INT2FIX(1));
}
range_reverse_each_positive_bignum_section(beg, end);
range_reverse_each_fixnum_section(beg, end);
range_reverse_each_negative_bignum_section(beg, end);
}
else {
return rb_call_super(0, NULL);
}
return range;
}
With a block given, passes each element of self to the block in reverse order:
a = [] (1..4).reverse_each {|element| a.push(element) } # => 1..4 a # => [4, 3, 2, 1] a = [] (1...4).reverse_each {|element| a.push(element) } # => 1...4 a # => [3, 2, 1]
With no block given, returns an enumerator.
static VALUE
range_size(VALUE range)
{
VALUE b = RANGE_BEG(range), e = RANGE_END(range);
if (RB_INTEGER_TYPE_P(b)) {
if (rb_obj_is_kind_of(e, rb_cNumeric)) {
return ruby_num_interval_step_size(b, e, INT2FIX(1), EXCL(range));
}
if (NIL_P(e)) {
return DBL2NUM(HUGE_VAL);
}
}
if (!discrete_object_p(b)) {
CANT_ITERATE_FROM(b);
}
return Qnil;
}
Returns the count of elements in self if both begin and end values are numeric; otherwise, returns nil:
(1..4).size # => 4 (1...4).size # => 3 (1..).size # => Infinity ('a'..'z').size # => nil
If self is not iterable, raises an exception:
(0.5..2.5).size # TypeError (..1).size # TypeError
Related: Range#count.
(?Numeric | int) → Enumerator[E, self]
(?Numeric | int) { (E element) → void } → self
(untyped) → Enumerator[E, self]
(untyped) { (E element) → void } → self
Source
static VALUE
range_step(int argc, VALUE *argv, VALUE range)
{
VALUE b, e, v, step;
int c, dir;
b = RANGE_BEG(range);
e = RANGE_END(range);
v = b;
const VALUE b_num_p = rb_obj_is_kind_of(b, rb_cNumeric);
const VALUE e_num_p = rb_obj_is_kind_of(e, rb_cNumeric);
// For backward compatibility reasons (conforming to behavior before 3.4), String/Symbol
// supports both old behavior ('a'..).step(1) and new behavior ('a'..).step('a')
// Hence the additional conversion/additional checks.
const VALUE str_b = rb_check_string_type(b);
const VALUE sym_b = SYMBOL_P(b) ? rb_sym2str(b) : Qnil;
if (rb_check_arity(argc, 0, 1))
step = argv[0];
else {
if (b_num_p || !NIL_P(str_b) || !NIL_P(sym_b) || (NIL_P(b) && e_num_p))
step = INT2FIX(1);
else
rb_raise(rb_eArgError, "step is required for non-numeric ranges");
}
const VALUE step_num_p = rb_obj_is_kind_of(step, rb_cNumeric);
if (step_num_p && b_num_p && rb_equal(step, INT2FIX(0))) {
rb_raise(rb_eArgError, "step can't be 0");
}
if (!rb_block_given_p()) {
// This code is allowed to create even beginless ArithmeticSequence, which can be useful,
// e.g., for array slicing:
// ary[(..-1) % 3]
if (step_num_p && ((b_num_p && (NIL_P(e) || e_num_p)) || (NIL_P(b) && e_num_p))) {
return rb_arith_seq_new(range, ID2SYM(rb_frame_this_func()), argc, argv,
range_step_size, b, e, step, EXCL(range));
}
// ...but generic Enumerator from beginless range is useless and probably an error.
if (NIL_P(b)) {
rb_raise(rb_eArgError, "#step for non-numeric beginless ranges is meaningless");
}
RETURN_SIZED_ENUMERATOR(range, argc, argv, 0);
}
if (NIL_P(b)) {
rb_raise(rb_eArgError, "#step iteration for beginless ranges is meaningless");
}
if (FIXNUM_P(b) && NIL_P(e) && FIXNUM_P(step)) {
/* perform summation of numbers in C until their reach Fixnum limit */
long i = FIX2LONG(b), unit = FIX2LONG(step);
do {
rb_yield(LONG2FIX(i));
i += unit; /* FIXABLE+FIXABLE never overflow */
} while (FIXABLE(i));
b = LONG2NUM(i);
/* then switch to Bignum API */
for (;; b = rb_big_plus(b, step))
rb_yield(b);
}
else if (FIXNUM_P(b) && FIXNUM_P(e) && FIXNUM_P(step)) {
/* fixnums are special: summation is performed in C for performance */
long end = FIX2LONG(e);
long i, unit = FIX2LONG(step);
if (unit < 0) {
if (!EXCL(range))
end -= 1;
i = FIX2LONG(b);
while (i > end) {
rb_yield(LONG2NUM(i));
i += unit;
}
}
else {
if (!EXCL(range))
end += 1;
i = FIX2LONG(b);
while (i < end) {
rb_yield(LONG2NUM(i));
i += unit;
}
}
}
else if (b_num_p && step_num_p && ruby_float_step(b, e, step, EXCL(range), TRUE)) {
/* done */
}
else if (!NIL_P(str_b) && FIXNUM_P(step)) {
// backwards compatibility behavior for String only, when no step/Integer step is passed
// See discussion in https://bugs.ruby-lang.org/issues/18368
VALUE iter[2] = {INT2FIX(1), step};
if (NIL_P(e)) {
rb_str_upto_endless_each(str_b, step_i, (VALUE)iter);
}
else {
rb_str_upto_each(str_b, e, EXCL(range), step_i, (VALUE)iter);
}
}
else if (!NIL_P(sym_b) && FIXNUM_P(step)) {
// same as above: backward compatibility for symbols
VALUE iter[2] = {INT2FIX(1), step};
if (NIL_P(e)) {
rb_str_upto_endless_each(sym_b, sym_step_i, (VALUE)iter);
}
else {
rb_str_upto_each(sym_b, rb_sym2str(e), EXCL(range), sym_step_i, (VALUE)iter);
}
}
else if (NIL_P(e)) {
// endless range
for (;; v = rb_funcall(v, id_plus, 1, step))
rb_yield(v);
}
else if (b_num_p && step_num_p && r_less(step, INT2FIX(0)) < 0) {
// iterate backwards, for consistency with ArithmeticSequence
if (EXCL(range)) {
for (; r_less(e, v) < 0; v = rb_funcall(v, id_plus, 1, step))
rb_yield(v);
}
else {
for (; (c = r_less(e, v)) <= 0; v = rb_funcall(v, id_plus, 1, step)) {
rb_yield(v);
if (!c) break;
}
}
}
else if ((dir = r_less(b, e)) == 0) {
if (!EXCL(range)) {
rb_yield(v);
}
}
else if (dir == r_less(b, rb_funcall(b, id_plus, 1, step))) {
// Direction of the comparison. We use it as a comparison operator in cycle:
// if begin < end, the cycle performs while value < end (iterating forward)
// if begin > end, the cycle performs while value > end (iterating backward with
// a negative step)
// One preliminary addition to check the step moves iteration in the same direction as
// from begin to end; otherwise, the iteration should be empty.
if (EXCL(range)) {
for (; r_less(v, e) == dir; v = rb_funcall(v, id_plus, 1, step))
rb_yield(v);
}
else {
for (; (c = r_less(v, e)) == dir || c == 0; v = rb_funcall(v, id_plus, 1, step)) {
rb_yield(v);
if (!c) break;
}
}
}
return range;
}
Iterates over the elements of range in steps of s. The iteration is performed by + operator:
(0..6).step(2) { puts _1 } # Prints: 0, 2, 4, 6 # Iterate between two dates in step of 1 day (24 hours) (Time.utc(2022, 2, 24)..Time.utc(2022, 3, 1)).step(24*60*60) { puts _1 } # Prints: # 2022-02-24 00:00:00 UTC # 2022-02-25 00:00:00 UTC # 2022-02-26 00:00:00 UTC # 2022-02-27 00:00:00 UTC # 2022-02-28 00:00:00 UTC # 2022-03-01 00:00:00 UTC
If + step decreases the value, iteration is still performed when step begin is higher than the end:
(0..6).step(-2) { puts _1 } # Prints nothing (6..0).step(-2) { puts _1 } # Prints: 6, 4, 2, 0 (Time.utc(2022, 3, 1)..Time.utc(2022, 2, 24)).step(-24*60*60) { puts _1 } # Prints: # 2022-03-01 00:00:00 UTC # 2022-02-28 00:00:00 UTC # 2022-02-27 00:00:00 UTC # 2022-02-26 00:00:00 UTC # 2022-02-25 00:00:00 UTC # 2022-02-24 00:00:00 UTC
When the block is not provided, and range boundaries and step are Numeric, the method returns Enumerator::ArithmeticSequence.
(1..5).step(2) # => ((1..5).step(2)) (1.0..).step(1.5) #=> ((1.0..).step(1.5)) (..3r).step(1/3r) #=> ((..3/1).step((1/3)))
Enumerator::ArithmeticSequence can be further used as a value object for iteration or slicing of collections (see Array#[]). There is a convenience method % with behavior similar to step to produce arithmetic sequences more expressively:
# Same as (1..5).step(2) (1..5) % 2 # => ((1..5).%(2))
In a generic case, when the block is not provided, Enumerator is returned:
('a'..).step('b') #=> #<Enumerator: "a"..:step("b")> ('a'..).step('b').take(3) #=> ["a", "ab", "abb"]
If s is not provided, it is considered 1 for ranges with numeric begin:
(1..5).step { p _1 } # Prints: 1, 2, 3, 4, 5
For non-Numeric ranges, step absence is an error:
(Time.utc(2022, 3, 1)..Time.utc(2022, 2, 24)).step { p _1 } # raises: step is required for non-numeric ranges (ArgumentError)
For backward compatibility reasons, String ranges support the iteration both with string step and with integer step. In the latter case, the iteration is performed by calculating the next values with String#succ:
('a'..'e').step(2) { p _1 } # Prints: a, c, e ('a'..'e').step { p _1 } # Default step 1; prints: a, b, c, d, e
Source
static VALUE
range_to_a(VALUE range)
{
if (NIL_P(RANGE_END(range))) {
rb_raise(rb_eRangeError, "cannot convert endless range to an array");
}
return rb_call_super(0, 0);
}
Returns an array containing the elements in self, if a finite collection; raises an exception otherwise.
(1..4).to_a # => [1, 2, 3, 4] (1...4).to_a # => [1, 2, 3] ('a'..'d').to_a # => ["a", "b", "c", "d"]
() → String
Source
static VALUE
range_to_s(VALUE range)
{
VALUE str, str2;
str = rb_obj_as_string(RANGE_BEG(range));
str2 = rb_obj_as_string(RANGE_END(range));
str = rb_str_dup(str);
rb_str_cat(str, "...", EXCL(range) ? 3 : 2);
rb_str_append(str, str2);
return str;
}
Returns a string representation of self, including begin.to_s and end.to_s:
(1..4).to_s # => "1..4" (1...4).to_s # => "1...4" (1..).to_s # => "1.." (..4).to_s # => "..4"
Note that returns from to_s and inspect may differ:
('a'..'d').to_s # => "a..d" ('a'..'d').inspect # => "\"a\"..\"d\""
Related: Range#inspect.
Source
static VALUE
range_to_set(VALUE range)
{
if (NIL_P(RANGE_END(range))) {
rb_raise(rb_eRangeError, "cannot convert endless range to a set");
}
return rb_call_super(0, NULL);
}
Returns a set containing the elements in self, if a finite collection; raises an exception otherwise.
(1..4).to_set # => Set[1, 2, 3, 4] (1...4).to_set # => Set[1, 2, 3] (1..).to_set # in 'Range#to_set': cannot convert endless range to a set (RangeError)