A Range represents an interval—a set of values with a start and an end. Ranges may be constructed using the s..e and s...e literals, or with Range::new. Ranges constructed using .. run from the start 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"]
Ranges can be constructed using objects of any type, as long as the objects can be compared using their <=> operator and they support the succ method to return the next object in sequence.
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
r = Xs.new(3)..Xs.new(6) #=> xxx..xxxxxx
r.to_a #=> [xxx, xxxx, xxxxx, xxxxxx]
r.member?(Xs.new(5)) #=> true
In the previous code example, class Xs includes the Comparable module. This is because Enumerable#member? checks for equality using ==. Including Comparable ensures that the == method is defined in terms of the <=> method implemented in Xs.
- #
- B
- E
-
- each,
- end,
- eql?,
- exclude_end?
- F
- H
- I
- L
- M
- N
- P
- S
- T
- Y
- Marshal START:includes
Constructs a range using the given start and end. If the third parameter is omitted or is false, the range will include the end object; otherwise, it will be excluded.
Source: show
static VALUE
range_initialize(argc, argv, range)
int argc;
VALUE *argv;
VALUE range;
{
VALUE beg, end, flags;
rb_scan_args(argc, argv, "21", &beg, &end, &flags);
/* Ranges are immutable, so that they should be initialized only once. */
if (rb_ivar_defined(range, id_beg)) {
rb_name_error(rb_intern("initialize"), "`initialize' called twice");
}
range_init(range, beg, end, RTEST(flags));
return Qnil;
}
Source: show
# File lib/yaml/rubytypes.rb, line 201 201: def Range.yaml_new( klass, tag, val ) 202: inr = %r'(\w+|[+-]?\d+(?:\.\d+)?(?:e[+-]\d+)?|"(?:[^\\"]|\\.)*")' 203: opts = {} 204: if String === val and val =~ /^#{inr}(\.{2,3})#{inr}$/o 205: r1, rdots, r2 = $1, $2, $3 206: opts = { 207: 'begin' => YAML.load( "--- #{r1}" ), 208: 'end' => YAML.load( "--- #{r2}" ), 209: 'excl' => rdots.length == 3 210: } 211: val = {} 212: elsif Hash === val 213: opts['begin'] = val.delete('begin') 214: opts['end'] = val.delete('end') 215: opts['excl'] = val.delete('excl') 216: end 217: if Hash === opts 218: r = YAML::object_maker( klass, {} ) 219: # Thank you, NaHi 220: Range.instance_method(:initialize). 221: bind(r). 222: call( opts['begin'], opts['end'], opts['excl'] ) 223: val.each { |k,v| r.instance_variable_set( k, v ) } 224: r 225: else 226: raise YAML::TypeError, "Invalid Range: " + val.inspect 227: end 228: end
Returns true only if obj is a Range, has equivalent beginning and end items (by comparing them with ==), and has the same exclude_end? setting as rng.
(0..2) == (0..2) #=> true (0..2) == Range.new(0,2) #=> true (0..2) == (0...2) #=> false
Source: show
static VALUE
range_eq(range, obj)
VALUE range, obj;
{
if (range == obj) return Qtrue;
if (!rb_obj_is_instance_of(obj, rb_obj_class(range)))
return Qfalse;
if (!rb_equal(rb_ivar_get(range, id_beg), rb_ivar_get(obj, id_beg)))
return Qfalse;
if (!rb_equal(rb_ivar_get(range, id_end), rb_ivar_get(obj, id_end)))
return Qfalse;
if (EXCL(range) != EXCL(obj)) return Qfalse;
return Qtrue;
}
Returns true if obj is an element of rng, 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
Source: show
static VALUE
range_include(range, val)
VALUE range, val;
{
VALUE beg, end;
beg = rb_ivar_get(range, id_beg);
end = rb_ivar_get(range, id_end);
if (r_le(beg, val)) {
if (EXCL(range)) {
if (r_lt(val, end)) return Qtrue;
}
else {
if (r_le(val, end)) return Qtrue;
}
}
return Qfalse;
}
Returns the first object in rng.
Source: show
static VALUE
range_first(range)
VALUE range;
{
return rb_ivar_get(range, id_beg);
}
Iterates over the elements rng, passing each in turn to the block. You can only iterate if the start object of the range supports the succ method (which means that you can’t iterate over ranges of Float objects).
(10..15).each do |n|
print n, ' '
end
produces:
10 11 12 13 14 15
Source: show
static VALUE
range_each(range)
VALUE range;
{
VALUE beg, end;
RETURN_ENUMERATOR(range, 0, 0);
beg = rb_ivar_get(range, id_beg);
end = rb_ivar_get(range, id_end);
if (!rb_respond_to(beg, id_succ)) {
rb_raise(rb_eTypeError, "can't iterate from %s",
rb_obj_classname(beg));
}
if (FIXNUM_P(beg) && FIXNUM_P(end)) { /* fixnums are special */
long lim = FIX2LONG(end);
long i;
if (!EXCL(range)) lim += 1;
for (i=FIX2LONG(beg); i<lim; i++) {
rb_yield(LONG2NUM(i));
}
}
else if (TYPE(beg) == T_STRING) {
VALUE args[5], iter[2];
args[0] = beg; args[1] = end; args[2] = range;
iter[0] = INT2FIX(1); iter[1] = INT2FIX(1);
rb_iterate((VALUE(*)_((VALUE)))str_step, (VALUE)args, step_i,
(VALUE)iter);
}
else {
range_each_func(range, each_i, beg, end, NULL);
}
return range;
}
Returns the object that defines the end of rng.
(1..10).end #=> 10 (1...10).end #=> 10
Source: show
static VALUE
range_last(range)
VALUE range;
{
return rb_ivar_get(range, id_end);
}
Returns true only if obj is a Range, has equivalent beginning and end items (by comparing them with eql?), and has the same exclude_end? setting as rng.
(0..2) == (0..2) #=> true (0..2) == Range.new(0,2) #=> true (0..2) == (0...2) #=> false
Source: show
static VALUE
range_eql(range, obj)
VALUE range, obj;
{
if (range == obj) return Qtrue;
if (!rb_obj_is_instance_of(obj, rb_obj_class(range)))
return Qfalse;
if (!rb_eql(rb_ivar_get(range, id_beg), rb_ivar_get(obj, id_beg)))
return Qfalse;
if (!rb_eql(rb_ivar_get(range, id_end), rb_ivar_get(obj, id_end)))
return Qfalse;
if (EXCL(range) != EXCL(obj)) return Qfalse;
return Qtrue;
}
Returns true if rng excludes its end value.
Source: show
static VALUE
range_exclude_end_p(range)
VALUE range;
{
return EXCL(range) ? Qtrue : Qfalse;
}
Returns the first object in rng.
Source: show
static VALUE
range_first(range)
VALUE range;
{
return rb_ivar_get(range, id_beg);
}
Generate a hash value such that two ranges with the same start and end points, and the same value for the “exclude end” flag, generate the same hash value.
Source: show
static VALUE
range_hash(range)
VALUE range;
{
long hash = EXCL(range);
VALUE v;
v = rb_hash(rb_ivar_get(range, id_beg));
hash ^= v << 1;
v = rb_hash(rb_ivar_get(range, id_end));
hash ^= v << 9;
hash ^= EXCL(range) << 24;
return LONG2FIX(hash);
}
Returns true if obj is an element of rng, 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
Source: show
static VALUE
range_include(range, val)
VALUE range, val;
{
VALUE beg, end;
beg = rb_ivar_get(range, id_beg);
end = rb_ivar_get(range, id_end);
if (r_le(beg, val)) {
if (EXCL(range)) {
if (r_lt(val, end)) return Qtrue;
}
else {
if (r_le(val, end)) return Qtrue;
}
}
return Qfalse;
}
Convert this range object to a printable form (using inspect to convert the start and end objects).
Source: show
static VALUE
range_inspect(range)
VALUE range;
{
VALUE str, str2;
str = rb_inspect(rb_ivar_get(range, id_beg));
str2 = rb_inspect(rb_ivar_get(range, id_end));
str = rb_str_dup(str);
rb_str_cat(str, "...", EXCL(range)?3:2);
rb_str_append(str, str2);
OBJ_INFECT(str, str2);
return str;
}
Returns the object that defines the end of rng.
(1..10).end #=> 10 (1...10).end #=> 10
Source: show
static VALUE
range_last(range)
VALUE range;
{
return rb_ivar_get(range, id_end);
}
Returns true if obj is an element of rng, 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
Source: show
static VALUE
range_include(range, val)
VALUE range, val;
{
VALUE beg, end;
beg = rb_ivar_get(range, id_beg);
end = rb_ivar_get(range, id_end);
if (r_le(beg, val)) {
if (EXCL(range)) {
if (r_lt(val, end)) return Qtrue;
}
else {
if (r_le(val, end)) return Qtrue;
}
}
return Qfalse;
}
Source: show
# File lib/pp.rb, line 351 351: def pretty_print(q) 352: q.pp self.begin 353: q.breakable '' 354: q.text(self.exclude_end? ? '...' : '..') 355: q.breakable '' 356: q.pp self.end 357: end
Iterates over rng, passing each nth element to the block. If the range contains numbers, n is added for each iteration. Otherwise step invokes succ to iterate through range elements. The following code uses class Xs, which is defined in the class-level documentation.
range = Xs.new(1)..Xs.new(10)
range.step(2) {|x| puts x}
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
Source: show
static VALUE
range_step(argc, argv, range)
int argc;
VALUE *argv;
VALUE range;
{
VALUE b, e, step, tmp;
RETURN_ENUMERATOR(range, argc, argv);
b = rb_ivar_get(range, id_beg);
e = rb_ivar_get(range, id_end);
if (argc == 0) {
step = INT2FIX(1);
}
else {
rb_scan_args(argc, argv, "01", &step);
if (!rb_obj_is_kind_of(step, rb_cNumeric)) {
step = rb_to_int(step);
}
if (rb_funcall(step, '<', 1, INT2FIX(0))) {
rb_raise(rb_eArgError, "step can't be negative");
}
else if (!rb_funcall(step, '>', 1, INT2FIX(0))) {
rb_raise(rb_eArgError, "step can't be 0");
}
}
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 (ruby_float_step(b, e, step, EXCL(range))) {
/* 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) ? '<' : rb_intern("<=");
while (RTEST(rb_funcall(b, op, 1, e))) {
rb_yield(b);
b = rb_funcall(b, '+', 1, step);
}
}
else {
tmp = rb_check_string_type(b);
if (!NIL_P(tmp)) {
VALUE args[5], iter[2];
b = tmp;
args[0] = e;
args[1] = EXCL(range) ? Qtrue : Qfalse;
iter[0] = INT2FIX(1);
iter[1] = step;
rb_block_call(b, rb_intern("upto"), 2, args, step_i, (VALUE)iter);
}
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 c = EXCL(range) ? '<' : rb_intern("<=");
while (RTEST(rb_funcall(b, c, 1, e))) {
rb_yield(b);
b = rb_funcall(b, '+', 1, step);
}
}
else {
VALUE args[2];
if (!rb_respond_to(b, id_succ)) {
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, b, e, args);
}
}
return range;
}
Convert this range object to a printable form.
Source: show
static VALUE
range_to_s(range)
VALUE range;
{
VALUE str, str2;
str = rb_obj_as_string(rb_ivar_get(range, id_beg));
str2 = rb_obj_as_string(rb_ivar_get(range, id_end));
str = rb_str_dup(str);
rb_str_cat(str, "...", EXCL(range)?3:2);
rb_str_append(str, str2);
OBJ_INFECT(str, str2);
return str;
}
Source: show
# File lib/yaml/rubytypes.rb, line 229 229: def to_yaml( opts = {} ) 230: YAML::quick_emit( self, opts ) do |out| 231: # if self.begin.is_complex_yaml? or self.begin.respond_to? :to_str or 232: # self.end.is_complex_yaml? or self.end.respond_to? :to_str or 233: # not to_yaml_properties.empty? 234: out.map( taguri, to_yaml_style ) do |map| 235: map.add( 'begin', self.begin ) 236: map.add( 'end', self.end ) 237: map.add( 'excl', self.exclude_end? ) 238: to_yaml_properties.each do |m| 239: map.add( m, instance_variable_get( m ) ) 240: end 241: end 242: # else 243: # out.scalar( taguri ) do |sc| 244: # sc.embed( self.begin ) 245: # sc.concat( self.exclude_end? ? "..." : ".." ) 246: # sc.embed( self.end ) 247: # end 248: # end 249: end 250: end