• range.c

Quicksearch

# Range

A Range represents an interval—a set of values with a beginning and an end. Ranges may be constructed using the s`..`e and s`...`e literals, or with ::new. Ranges constructed using `..` run from the beginning to the end inclusively. Those created using `...` exclude the end value. When used as an iterator, ranges return each value in the sequence.

```(-1..-5).to_a      #=> []
(-5..-1).to_a      #=> [-5, -4, -3, -2, -1]
('a'..'e').to_a    #=> ["a", "b", "c", "d", "e"]
('a'...'e').to_a   #=> ["a", "b", "c", "d"]
```

## Beginless/Endless Ranges¶ ↑

A “beginless range” and “endless range” represents a semi-infinite range. Literal notation for a beginless range is:

```(..1)
# or
(...1)```

Literal notation for an endless range is:

```(1..)
# or similarly
(1...)```

Which is equivalent to

```(1..nil)  # or similarly (1...nil)
Range.new(1, nil) # or Range.new(1, nil, true)
```

Beginless/endless ranges are useful, for example, for idiomatic slicing of arrays:

```[1, 2, 3, 4, 5][...2]   # => [1, 2]
[1, 2, 3, 4, 5][2...]   # => [3, 4, 5]```

Some implementation details:

• `begin` of beginless range and `end` of endless range are `nil`;

• `each` of beginless range raises an exception;

• `each` of endless range enumerates infinite sequence (may be useful in combination with Enumerable#take_while or similar methods);

• `(1..)` and `(1...)` are not equal, although technically representing the same sequence.

## Custom Objects in Ranges¶ ↑

Ranges can be constructed using any objects that can be compared using the `<=>` operator. Methods that treat the range as a sequence (#each and methods inherited from Enumerable) expect the begin object to implement a `succ` method to return the next object in sequence. The step and include? methods require the begin object to implement `succ` or to be numeric.

In the `Xs` class below both `<=>` and `succ` are implemented so `Xs` can be used to construct ranges. Note that the Comparable module is included so the `==` method is defined in terms of `<=>`.

```class Xs                # represent a string of 'x's
include Comparable
attr :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
```

An example of using `Xs` to construct a range:

```r = Xs.new(3)..Xs.new(6)   #=> xxx..xxxxxx
r.to_a                     #=> [xxx, xxxx, xxxxx, xxxxxx]
r.member?(Xs.new(5))       #=> true
```

### Public Class Methods

new(begin, end, exclude_end=false) → rng click to toggle source

Constructs a range using the given `begin` and `end`. If the `exclude_end` parameter is omitted or is `false`, the range will include the end object; otherwise, it will be excluded.

```
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;
}
```

### Public Instance Methods

step(n=1) {| obj | block } → rng click to toggle source
step(n=1) → an_enumerator
step(n=1) → an_arithmetic_sequence
rng % n → an_enumerator
rng % n → an_arithmetic_sequence

Iterates over the range, passing each `n`th element to the block. If begin and end are numeric, `n` is added for each iteration. Otherwise step invokes succ to iterate through range elements.

If no block is given, an enumerator is returned instead. Especially, the enumerator is an Enumerator::ArithmeticSequence if begin and end of the range are numeric.

```range = Xs.new(1)..Xs.new(10)
range.step(2) {|x| puts x}
puts
range.step(3) {|x| puts x}
```

produces:

``` 1 x
3 xxx
5 xxxxx
7 xxxxxxx
9 xxxxxxxxx

1 x
4 xxxx
7 xxxxxxx
10 xxxxxxxxxx```

See Range for the definition of class Xs.

```
static VALUE
range_percent_step(VALUE range, VALUE step)
{
return range_step(1, &step, range);
}
```
rng == obj → true or false click to toggle source

Returns `true` only if `obj` is a Range, has equivalent begin and end items (by comparing them with `==`), and has the same exclude_end? setting as the range.

```(0..2) == (0..2)            #=> true
(0..2) == Range.new(0,2)    #=> true
(0..2) == (0...2)           #=> false
```
```
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);
}
```
rng === obj → true or false click to toggle source

Returns `true` if `obj` is an element of the range, `false` otherwise. Conveniently, `===` is the comparison operator used by `case` statements.

```case 79
when 1..50   then   print "low\n"
when 51..75  then   print "medium\n"
when 76..100 then   print "high\n"
end
```

produces:

```high
```
```
static VALUE
range_eqq(VALUE range, VALUE val)
{
VALUE ret = range_include_internal(range, val);
if (ret != Qundef) return ret;
return r_cover_p(range, RANGE_BEG(range), RANGE_END(range), val);
}
```
begin → obj click to toggle source

Returns the object that defines the beginning of the range.

```(1..10).begin   #=> 1
```
```
static VALUE
range_begin(VALUE range)
{
return RANGE_BEG(range);
}
```
bsearch {|obj| block } → value click to toggle source

By using binary search, finds a value in range which meets the given condition in O(log n) where n is the size of the range.

You can use this method in two use cases: a find-minimum mode and a find-any mode. In either case, the elements of the range must be monotone (or sorted) with respect to the block.

In find-minimum mode (this is a good choice for typical use case), the block must return true or false, and there must be a value x so that:

• the block returns false for any value which is less than x, and

• the block returns true for any value which is greater than or equal to x.

If x is within the range, this method returns the value x. Otherwise, it returns nil.

```ary = [0, 4, 7, 10, 12]
(0...ary.size).bsearch {|i| ary[i] >= 4 } #=> 1
(0...ary.size).bsearch {|i| ary[i] >= 6 } #=> 2
(0...ary.size).bsearch {|i| ary[i] >= 8 } #=> 3
(0...ary.size).bsearch {|i| ary[i] >= 100 } #=> nil

(0.0...Float::INFINITY).bsearch {|x| Math.log(x) >= 0 } #=> 1.0
```

In find-any mode (this behaves like libc's bsearch(3)), the block must return a number, and there must be two values x and y (x <= y) so that:

• the block returns a positive number for v if v < x,

• the block returns zero for v if x <= v < y, and

• the block returns a negative number for v if y <= v.

This method returns any value which is within the intersection of the given range and x…y (if any). If there is no value that satisfies the condition, it returns nil.

```ary = [0, 100, 100, 100, 200]
(0..4).bsearch {|i| 100 - ary[i] } #=> 1, 2 or 3
(0..4).bsearch {|i| 300 - ary[i] } #=> nil
(0..4).bsearch {|i|  50 - ary[i] } #=> nil
```

You must not mix the two modes at a time; the block must always return either true/false, or always return a number. It is undefined which value is actually picked up at each iteration.

```
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)
* as well as the fact that -1/2 can be 0 or -1 in C89.
*
* 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) \
do { \
RETURN_ENUMERATOR(range, 0, 0); \
if (EXCL(range)) high--; \
org_high = high; \
while (low < high) { \
mid = ((high < 0) == (low < 0)) ? low + ((high - low) / 2) \
: (low < -high) ? -((-1 - low - high)/2 + 1) : (low + high) / 2; \
BSEARCH_CHECK(conv(mid)); \
if (smaller) { \
high = mid; \
} \
else { \
low = mid + 1; \
} \
} \
if (low == org_high) { \
BSEARCH_CHECK(conv(low)); \
if (!smaller) return Qnil; \
} \
return satisfied; \
} while (0)

beg = RANGE_BEG(range);
end = RANGE_END(range);

if (FIXNUM_P(beg) && FIXNUM_P(end)) {
long low = FIX2LONG(beg);
long high = FIX2LONG(end);
long mid, org_high;
BSEARCH(INT2FIX);
}
#if SIZEOF_DOUBLE == 8 && defined(HAVE_INT64_T)
else if (RB_TYPE_P(beg, T_FLOAT) || RB_TYPE_P(end, T_FLOAT)) {
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, org_high;
BSEARCH(int64_as_double_to_num);
}
#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) {
return bsearch_integer_range(beg, mid, 0);
}
diff = rb_funcall(diff, '*', 1, LONG2FIX(2));
}
}
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) {
return bsearch_integer_range(mid, end, 0);
}
diff = rb_funcall(diff, '*', 1, LONG2FIX(2));
}
}
else {
rb_raise(rb_eTypeError, "can't do binary search for %s", rb_obj_classname(beg));
}
return range;
}
```
cover?(obj) → true or false click to toggle source
cover?(range) → true or false

Returns `true` if `obj` is between the begin and end of the range.

This tests `begin <= obj <= end` when exclude_end? is `false` and `begin <= obj < end` when exclude_end? is `true`.

If called with a Range argument, returns `true` when the given range is covered by the receiver, by comparing the begin and end values. If the argument can be treated as a sequence, this method treats it that way. In the specific case of `(a..b).cover?(c...d)` with ```a <= c && b < d```, the end of the sequence must be calculated, which may exhibit poor performance if `c` is non-numeric. Returns `false` if the begin value of the range is larger than the end value.

```("a".."z").cover?("c")  #=> true
("a".."z").cover?("5")  #=> false
("a".."z").cover?("cc") #=> true
(1..5).cover?(2..3)     #=> true
(1..5).cover?(0..6)     #=> false
(1..5).cover?(1...6)    #=> true
```
```
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);
}
```
each {| i | block } → rng click to toggle source
each → an_enumerator

Iterates over the elements of range, passing each in turn to the block.

The `each` method can only be used if the begin object of the range supports the `succ` method. A TypeError is raised if the object does not have `succ` method defined (like Float).

If no block is given, an enumerator is returned instead.

```(10..15).each {|n| print n, ' ' }
# prints: 10 11 12 13 14 15

(2.5..5).each {|n| print n, ' ' }
# raises: TypeError: can't iterate from Float
```
```
static VALUE
range_each(VALUE range)
{
VALUE beg, end;
long i, lim;

RETURN_SIZED_ENUMERATOR(range, 0, 0, range_enum_size);

beg = RANGE_BEG(range);
end = RANGE_END(range);

if (FIXNUM_P(beg) && NIL_P(end)) {
fixnum_endless:
i = FIX2LONG(beg);
while (FIXABLE(i)) {
rb_yield(LONG2FIX(i++));
}
beg = LONG2NUM(i);
bignum_endless:
for (;; beg = rb_big_plus(beg, INT2FIX(1)))
rb_yield(beg);
}
else if (FIXNUM_P(beg) && FIXNUM_P(end)) { /* fixnums are special */
fixnum_loop:
lim = FIX2LONG(end);
if (!EXCL(range))
lim += 1;
for (i = FIX2LONG(beg); i < lim; i++) {
rb_yield(LONG2FIX(i));
}
}
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)) goto fixnum_endless;
if (FIXNUM_P(end)) goto fixnum_loop;
}
else {
if (NIL_P(end)) goto bignum_endless;
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;
}
```
end → obj click to toggle source

Returns the object that defines the end of the range.

```(1..10).end    #=> 10
(1...10).end   #=> 10
```
```
static VALUE
range_end(VALUE range)
{
return RANGE_END(range);
}
```
entries → array click to toggle source

Returns an array containing the items in the range.

```(1..7).to_a  #=> [1, 2, 3, 4, 5, 6, 7]
(1..).to_a   #=> RangeError: cannot convert endless range to an array```
```
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);
}
```
eql?(obj) → true or false click to toggle source

Returns `true` only if `obj` is a Range, has equivalent begin and end items (by comparing them with `eql?`), and has the same exclude_end? setting as the range.

```(0..2).eql?(0..2)            #=> true
(0..2).eql?(Range.new(0,2))  #=> true
(0..2).eql?(0...2)           #=> false
```
```
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);
}
```
exclude_end? → true or false click to toggle source

Returns `true` if the range excludes its end value.

```(1..5).exclude_end?     #=> false
(1...5).exclude_end?    #=> true
```
```
static VALUE
range_exclude_end_p(VALUE range)
{
return EXCL(range) ? Qtrue : Qfalse;
}
```
first → obj click to toggle source
first(n) → an_array

Returns the first object in the range, or an array of the first `n` elements.

```(10..20).first     #=> 10
(10..20).first(3)  #=> [10, 11, 12]
```
```
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(NUM2LONG(n));
rb_block_call(range, idEach, 0, 0, first_i, (VALUE)ary);

return ary[1];
}
```
hash → integer click to toggle source

Compute a hash-code for this range. Two ranges with equal begin and end points (using `eql?`), and the same exclude_end? value will generate the same hash-code.

```
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);
}
```
include?(obj) → true or false click to toggle source

Returns `true` if `obj` is an element of the range, `false` otherwise. If begin and end are numeric, comparison is done according to the magnitude of the values.

```("a".."z").include?("g")   #=> true
("a".."z").include?("A")   #=> false
("a".."z").include?("cc")  #=> false
```
```
static VALUE
range_include(VALUE range, VALUE val)
{
VALUE ret = range_include_internal(range, val);
if (ret != Qundef) return ret;
return rb_call_super(1, &val);
}
```
inspect → string click to toggle source

Convert this range object to a printable form (using inspect to convert the begin and end objects).

```
static VALUE
range_inspect(VALUE range)
{
return rb_exec_recursive(inspect_range, range, 0);
}
```
last → obj click to toggle source
last(n) → an_array

Returns the last object in the range, or an array of the last `n` elements.

Note that with no arguments `last` will return the object that defines the end of the range even if exclude_end? is `true`.

```(10..20).last      #=> 20
(10...20).last     #=> 20
(10..20).last(3)   #=> [18, 19, 20]
(10...20).last(3)  #=> [17, 18, 19]
```
```
static VALUE
range_last(int argc, VALUE *argv, VALUE range)
{
VALUE b, e;

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);

b = RANGE_BEG(range);
e = RANGE_END(range);
if (RB_INTEGER_TYPE_P(b) && RB_INTEGER_TYPE_P(e) &&
RB_LIKELY(rb_method_basic_definition_p(rb_cRange, idEach))) {
return rb_int_range_last(argc, argv, range);
}
return rb_ary_last(argc, argv, rb_Array(range));
}
```
max → obj click to toggle source
max {| a,b | block } → obj
max(n) → obj
max(n) {| a,b | block } → obj

Returns the maximum value in the range. Returns `nil` if the begin value of the range larger than the end value. Returns `nil` if the begin value of an exclusive range is equal to the end value.

Can be given an optional block to override the default comparison method `a <=> b`.

```(10..20).max    #=> 20
```
```
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");
}

if (rb_block_given_p() || (EXCL(range) && !nm) || argc) {
return rb_call_super(argc, argv);
}
else {
struct cmp_opt_data cmp_opt = { 0, 0 };
VALUE b = RANGE_BEG(range);
int c = OPTIMIZED_CMP(b, e, cmp_opt);

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 (!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_funcall(e, '-', 1, INT2FIX(1));
}
return e;
}
}
```
member?(obj) → true or false click to toggle source

Returns `true` if `obj` is an element of the range, `false` otherwise. If begin and end are numeric, comparison is done according to the magnitude of the values.

```("a".."z").include?("g")   #=> true
("a".."z").include?("A")   #=> false
("a".."z").include?("cc")  #=> false
```
```
static VALUE
range_include(VALUE range, VALUE val)
{
VALUE ret = range_include_internal(range, val);
if (ret != Qundef) return ret;
return rb_call_super(1, &val);
}
```
min → obj click to toggle source
min {| a,b | block } → obj
min(n) → array
min(n) {| a,b | block } → array

Returns the minimum value in the range. Returns `nil` if the begin value of the range is larger than the end value. Returns `nil` if the begin value of an exclusive range is equal to the end value.

Can be given an optional block to override the default comparison method `a <=> b`.

```(10..20).min    #=> 10
```
```
static VALUE
range_min(int argc, VALUE *argv, VALUE 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 {
struct cmp_opt_data cmp_opt = { 0, 0 };
VALUE b = RANGE_BEG(range);
VALUE e = RANGE_END(range);
int c = NIL_P(e) ? -1 : OPTIMIZED_CMP(b, e, cmp_opt);

if (c > 0 || (c == 0 && EXCL(range)))
return Qnil;
return b;
}
}
```
size → num click to toggle source

Returns the number of elements in the range. Both the begin and the end of the Range must be Numeric, otherwise nil is returned.

```(10..20).size    #=> 11
('a'..'z').size  #=> nil
(-Float::INFINITY..Float::INFINITY).size #=> Infinity
```
```
static VALUE
range_size(VALUE range)
{
VALUE b = RANGE_BEG(range), e = RANGE_END(range);
if (rb_obj_is_kind_of(b, rb_cNumeric)) {
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);
}
}
else if (NIL_P(b)) {
return DBL2NUM(HUGE_VAL);
}

return Qnil;
}
```
step(n=1) {| obj | block } → rng click to toggle source
step(n=1) → an_enumerator
step(n=1) → an_arithmetic_sequence
rng % n → an_enumerator
rng % n → an_arithmetic_sequence
`%`

Iterates over the range, passing each `n`th element to the block. If begin and end are numeric, `n` is added for each iteration. Otherwise step invokes succ to iterate through range elements.

If no block is given, an enumerator is returned instead. Especially, the enumerator is an Enumerator::ArithmeticSequence if begin and end of the range are numeric.

```range = Xs.new(1)..Xs.new(10)
range.step(2) {|x| puts x}
puts
range.step(3) {|x| puts x}
```

produces:

``` 1 x
3 xxx
5 xxxxx
7 xxxxxxx
9 xxxxxxxxx

1 x
4 xxxx
7 xxxxxxx
10 xxxxxxxxxx```

See Range for the definition of class Xs.

```
static VALUE
range_step(int argc, VALUE *argv, VALUE range)
{
VALUE b, e, step, tmp;

b = RANGE_BEG(range);
e = RANGE_END(range);
step = (!rb_check_arity(argc, 0, 1) ? INT2FIX(1) : argv[0]);

if (!rb_block_given_p()) {
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);
if ((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));
}

RETURN_SIZED_ENUMERATOR(range, argc, argv, range_step_size);
}

step = check_step_domain(step);

if (FIXNUM_P(b) && NIL_P(e) && FIXNUM_P(step)) {
long i = FIX2LONG(b), unit = FIX2LONG(step);
do {
rb_yield(LONG2FIX(i));
i += unit;          /* FIXABLE+FIXABLE never overflow */
} while (FIXABLE(i));
b = LONG2NUM(i);

for (;; b = rb_big_plus(b, step))
rb_yield(b);
}
else if (FIXNUM_P(b) && FIXNUM_P(e) && FIXNUM_P(step)) { /* fixnums are special */
long end = FIX2LONG(e);
long i, unit = FIX2LONG(step);

if (!EXCL(range))
end += 1;
i = FIX2LONG(b);
while (i < end) {
rb_yield(LONG2NUM(i));
if (i + unit < i) break;
i += unit;
}

}
else if (SYMBOL_P(b) && (NIL_P(e) || SYMBOL_P(e))) { /* symbols are special */
VALUE iter[2];
iter[0] = INT2FIX(1);
iter[1] = step;

b = rb_sym2str(b);
if (NIL_P(e)) {
rb_str_upto_endless_each(b, sym_step_i, (VALUE)iter);
}
else {
rb_str_upto_each(b, rb_sym2str(e), EXCL(range), sym_step_i, (VALUE)iter);
}
}
else if (ruby_float_step(b, e, step, EXCL(range), TRUE)) {
/* done */
}
else if (rb_obj_is_kind_of(b, rb_cNumeric) ||
!NIL_P(rb_check_to_integer(b, "to_int")) ||
!NIL_P(rb_check_to_integer(e, "to_int"))) {
ID op = EXCL(range) ? '<' : idLE;
VALUE v = b;
int i = 0;

while (NIL_P(e) || RTEST(rb_funcall(v, op, 1, e))) {
rb_yield(v);
i++;
v = rb_funcall(b, '+', 1, rb_funcall(INT2NUM(i), '*', 1, step));
}
}
else {
tmp = rb_check_string_type(b);

if (!NIL_P(tmp)) {
VALUE iter[2];

b = tmp;
iter[0] = INT2FIX(1);
iter[1] = step;

if (NIL_P(e)) {
rb_str_upto_endless_each(b, step_i, (VALUE)iter);
}
else {
rb_str_upto_each(b, e, EXCL(range), step_i, (VALUE)iter);
}
}
else {
VALUE args[2];

if (!discrete_object_p(b)) {
rb_raise(rb_eTypeError, "can't iterate from %s",
rb_obj_classname(b));
}
args[0] = INT2FIX(1);
args[1] = step;
range_each_func(range, step_i, (VALUE)args);
}
}
return range;
}
```
to_a → array click to toggle source

Returns an array containing the items in the range.

```(1..7).to_a  #=> [1, 2, 3, 4, 5, 6, 7]
(1..).to_a   #=> RangeError: cannot convert endless range to an array```
```
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);
}
```
to_s → string click to toggle source

Convert this range object to a printable form (using to_s to convert the begin and end objects).

```
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);
OBJ_INFECT(str, range);

return str;
}
```