Ruby 2.4

Range

class Range

父类:ObjectIncluded模块:Enumerable

A Range表示间隔 - 一组具有开始和结束的值。范围可以使用被构造小号..Ë小号...ë文字,或与新::。使用..从头到尾包含运行的构建范围。使用创建的那些...排除最终值。当用作迭代器时,范围返回序列中的每个值。

(-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"]

范围中的自定义对象

范围可以使用任何可以使用<=>操作符进行比较的对象来构造。将范围视为序列的方法(#each和从Enumerable继承的方法)期望begin对象实现一个succ方法来按顺序返回下一个对象。该步骤包括?方法需要begin对象来实现succ或是数字。

在Xs以下两个类<=>和succ被实现,以便Xs可以用于构建范围。请注意,包含了可比较模块,因此该==方法按照定义<=>。

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

使用Xs构建范围的示例:

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

公共类方法

json_create(object) Show source

通过构建带有a序列化参数的新Range对象来反序列化JSON字符串to_json

# File ext/json/lib/json/add/range.rb, line 10 def self.json_create(object) new(*object['a']) end

new(begin, end, exclude_end=false) → rng Show source

使用给定的begin和构造一个范围end。如果exclude_end参数被省略或者是false,那么rng将包括结束对象; 否则,它将被排除。

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

公共实例方法

rng == obj → true or false Show source

true仅当obj是范围时才返回,具有相同的开始和结束项目(通过比较它们==),并且具有相同的exclude_end?设置为范围。

(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 Show source

true如果obj是范围的元素则返回,false否则返回。方便的===是,case报表使用的是比较运算符。

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

生产:

high

static VALUE range_eqq(VALUE range, VALUE val) { return rb_funcall(range, rb_intern("include?"), 1, val }

as_json(*) Show source

返回一个散列,它将变成一个JSON对象并表示这个对象。

# File ext/json/lib/json/add/range.rb, line 16 def as_json(*) { JSON.create_id => self.class.name, 'a' => [ first, last, exclude_end? ] } end

begin → obj Show source

返回定义范围起点的对象。

(1..10).begin #=> 1

static VALUE range_begin(VALUE range) { return RANGE_BEG(range }

bsearch {|obj| block } → value Show source

通过使用二分查找,找到满足O(log n)中给定条件的范围内的值,其中n是该范围的大小。

您可以在两种使用情况下使用此方法:查找最小模式和查找任何模式。在任何一种情况下,范围的元素都必须是单调的(或排序)。

在find-minimum模式下(这对于典型用例来说是个不错的选择),块必须返回true或false,并且必须有一个值x,以便:

  • 对于任何小于x的值,块返回false

  • 对于任何大于或等于x的值,块返回true。

如果x在范围内,则此方法返回值x。否则,它返回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

在find-any模式下(这与libc的bsearch(3)相似),块必须返回一个数字,并且必须有两个值x和y(x <= y),以便:

  • 如果v <x,则块返回v的正数,

  • 如果x <= v <y,则块返回v

  • 如果y <= v,则块返回v的负数。

此方法返回给定范围和x ... y(如果有)的交集内的任何值。如果没有满足条件的值,则返回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

你不能一次混合两种模式; 该块必须始终返回true / false,或始终返回一个数字。它没有定义在每次迭代中实际选取哪个值。

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_CHECK(expr) \ do { \ VALUE val = (expr \ VALUE v = rb_yield(val \ if (FIXNUM_P(v)) { \ if (v == INT2FIX(0)) return val; \ smaller = (SIGNED_VALUE)v < 0; \ } \ else if (v == Qtrue) { \ satisfied = val; \ smaller = 1; \ } \ else if (v == Qfalse || v == Qnil) { \ smaller = 0; \ } \ else if (rb_obj_is_kind_of(v, rb_cNumeric)) { \ int cmp = rb_cmpint(rb_funcall(v, id_cmp, 1, INT2FIX(0)), v, INT2FIX(0) \ if (!cmp) return val; \ smaller = cmp < 0; \ } \ else { \ rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE \ " (must be numeric, true, false or nil)", \ rb_obj_class(v) \ } \ } while (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(RFLOAT_VALUE(rb_Float(beg)) int64_t high = double_as_int64(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)) { VALUE low = rb_to_int(beg VALUE high = rb_to_int(end VALUE mid, org_high; RETURN_ENUMERATOR(range, 0, 0 if (EXCL(range)) high = rb_funcall(high, '-', 1, INT2FIX(1) org_high = high; while (rb_cmpint(rb_funcall(low, id_cmp, 1, high), low, high) < 0) { mid = rb_funcall(rb_funcall(high, '+', 1, low), id_div, 1, INT2FIX(2) BSEARCH_CHECK(mid if (smaller) { high = mid; } else { low = rb_funcall(mid, '+', 1, INT2FIX(1) } } if (rb_equal(low, org_high)) { BSEARCH_CHECK(low if (!smaller) return Qnil; } return satisfied; } else { rb_raise(rb_eTypeError, "can't do binary search for %s", rb_obj_classname(beg) } return range; }

cover?(obj) → true or false Show source

true如果obj在范围的开始和结束之间返回。

这begin <= obj <= end在exclude_end时测试?是false和begin <= obj < end当exclude_end?是true。

("a".."z").cover?("c") #=> true ("a".."z").cover?("5") #=> false ("a".."z").cover?("cc") #=> true

static VALUE range_cover(VALUE range, VALUE val) { VALUE beg, end; beg = RANGE_BEG(range end = RANGE_END(range return r_cover_p(range, beg, end, val }

each {| i | block } → rng Show source

each → an_enumerator

迭代范围元素,将每个元素依次传递给块。

each方法只能在范围的开始对象支持该succ方法时使用。如果对象没有succ定义方法(如Float),则会引发TypeError 。

如果没有给出块,则返回一个枚举器。

(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; RETURN_SIZED_ENUMERATOR(range, 0, 0, range_enum_size beg = RANGE_BEG(range end = RANGE_END(range 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(LONG2FIX(i) } } else if (SYMBOL_P(beg) && SYMBOL_P(end)) { /* symbols are special */ VALUE args[2]; args[0] = rb_sym2str(end args[1] = EXCL(range) ? Qtrue : Qfalse; rb_block_call(rb_sym2str(beg), rb_intern("upto"), 2, args, sym_each_i, 0 } else { VALUE tmp = rb_check_string_type(beg if (!NIL_P(tmp)) { VALUE args[2]; args[0] = end; args[1] = EXCL(range) ? Qtrue : Qfalse; rb_block_call(tmp, rb_intern("upto"), 2, args, each_i, 0 } else { if (!discrete_object_p(beg)) { rb_raise(rb_eTypeError, "can't iterate from %s", rb_obj_classname(beg) } range_each_func(range, each_i, 0 } } return range; }

end → obj Show source

返回定义范围结束的对象。

(1..10).end #=> 10 (1...10).end #=> 10

static VALUE range_end(VALUE range) { return RANGE_END(range }

eql?(obj) → true or false Show source

true仅当obj是范围时才返回,具有相同的开始和结束项目(通过比较它们eql?),并且具有相同的exclude_end?设置为范围。

(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 Show source

true如果范围排除其最终值,则返回。

(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 Show source

first(n) → an_array

返回范围中的第一个对象或第一个n元素的数组。

(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 (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 Show source

计算此范围的散列码。两个范围具有相同的开始点和结束点(使用eql?),以及相同的exclude_end?值将生成相同的散列码。

See also Object#hash.

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 LONG2FIX(hash }

include?(obj) → true or false Show source

true如果obj是范围的元素则返回,false否则返回。如果开始和结束是数字,则根据值的大小进行比较。

("a".."z").include?("g") #=> true ("a".."z").include?("A") #=> false ("a".."z").include?("cc") #=> false

static VALUE range_include(VALUE range, VALUE val) { VALUE beg = RANGE_BEG(range VALUE end = RANGE_END(range int nv = FIXNUM_P(beg) || FIXNUM_P(end) || linear_object_p(beg) || linear_object_p(end if (nv || !NIL_P(rb_check_to_integer(beg, "to_int")) || !NIL_P(rb_check_to_integer(end, "to_int"))) { return r_cover_p(range, beg, end, val } else if (RB_TYPE_P(beg, T_STRING) && RB_TYPE_P(end, T_STRING)) { VALUE rb_str_include_range_p(VALUE beg, VALUE end, VALUE val, VALUE exclusive return rb_str_include_range_p(beg, end, val, RANGE_EXCL(range) } /* TODO: ruby_frame->this_func = rb_intern("include?" */ return rb_call_super(1, &val }

inspect → string Show source

将此范围对象转换为可打印格式(inspect用于转换开始和结束对象)。

static VALUE range_inspect(VALUE range) { return rb_exec_recursive(inspect_range, range, 0 }

last → obj Show source

last(n) → an_array

返回范围中的最后一个对象或最后一个n元素的数组。

请注意,没有参数last会返回定义范围结尾的对象,即使是exclude_end?是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) { if (argc == 0) return RANGE_END(range return rb_ary_last(argc, argv, rb_Array(range) }

max → obj Show source

max {| a,b | block } → obj

max(n) → obj

max(n) {| a,b | block } → obj

返回范围中的最大值。返回nil,如果开始比端值大的范围内的值。返回nil如果独占范围的开始值等于终值。

可以给定一个可选块来覆盖默认的比较方法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 (rb_block_given_p() || (EXCL(range) && !nm) || argc) { return rb_call_super(argc, argv } else { VALUE b = RANGE_BEG(range int c = rb_cmpint(rb_funcall(b, id_cmp, 1, e), b, e if (c > 0) return Qnil; if (EXCL(range)) { if (!FIXNUM_P(e) && !rb_obj_is_kind_of(e, rb_cInteger)) { rb_raise(rb_eTypeError, "cannot exclude non Integer end value" } if (c == 0) return Qnil; if (!FIXNUM_P(b) && !rb_obj_is_kind_of(b,rb_cInteger)) { 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 Show source

true如果obj是范围的元素则返回,false否则返回。如果开始和结束是数字,则根据值的大小进行比较。

("a".."z").include?("g") #=> true ("a".."z").include?("A") #=> false ("a".."z").include?("cc") #=> false

static VALUE range_include(VALUE range, VALUE val) { VALUE beg = RANGE_BEG(range VALUE end = RANGE_END(range int nv = FIXNUM_P(beg) || FIXNUM_P(end) || linear_object_p(beg) || linear_object_p(end if (nv || !NIL_P(rb_check_to_integer(beg, "to_int")) || !NIL_P(rb_check_to_integer(end, "to_int"))) { return r_cover_p(range, beg, end, val } else if (RB_TYPE_P(beg, T_STRING) && RB_TYPE_P(end, T_STRING)) { VALUE rb_str_include_range_p(VALUE beg, VALUE end, VALUE val, VALUE exclusive return rb_str_include_range_p(beg, end, val, RANGE_EXCL(range) } /* TODO: ruby_frame->this_func = rb_intern("include?" */ return rb_call_super(1, &val }

min → obj Show source

min {| a,b | block } → obj

min(n) → array

min(n) {| a,b | block } → array

返回范围中的最小值。返回nil如果该范围的开始值大于结束值大。返回nil如果独占范围的开始值等于终值。

可以给定一个可选块来覆盖默认的比较方法a <=> b。

(10..20).min #=> 10

static VALUE range_min(int argc, VALUE *argv, VALUE range) { if (rb_block_given_p()) { 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 = rb_cmpint(rb_funcall(b, id_cmp, 1, e), b, e if (c > 0 || (c == 0 && EXCL(range))) return Qnil; return b; } }

size → num Show source

返回范围中元素的数量。Range的开始和结束都必须是Numeric,否则返回nil。

(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) && rb_obj_is_kind_of(e, rb_cNumeric)) { return ruby_num_interval_step_size(b, e, INT2FIX(1), EXCL(range) } return Qnil; }

step(n=1) {| obj | block } → rng Show source

step(n=1) → an_enumerator

迭代整个范围,将每个n元素传递给块。如果开始和结束是数字,n则为每次迭代添加。否则,step调用succ遍历范围元素。

如果没有给出块,则返回一个枚举器。

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

生产:

1 x 3 xxx 5 xxxxx 7 xxxxxxx 9 xxxxxxxxx 1 x 4 xxxx 7 xxxxxxx 10 xxxxxxxxxx

有关类X的定义,请参阅范围。

static VALUE range_step(int argc, VALUE *argv, VALUE range) { VALUE b, e, step, tmp; RETURN_SIZED_ENUMERATOR(range, argc, argv, range_step_size b = RANGE_BEG(range e = RANGE_END(range if (argc == 0) { step = INT2FIX(1 } else { rb_scan_args(argc, argv, "01", &step step = check_step_domain(step } 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) && SYMBOL_P(e)) { /* symbols are special */ VALUE args[2], iter[2]; args[0] = rb_sym2str(e args[1] = EXCL(range) ? Qtrue : Qfalse; iter[0] = INT2FIX(1 iter[1] = step; rb_block_call(rb_sym2str(b), rb_intern("upto"), 2, args, sym_step_i, (VALUE)iter } 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) ? '<' : idLE; VALUE v = b; int i = 0; while (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 args[2], 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 { 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_json(*args) Show source

使用a包含first(整数),last(整数)和exclude_end?(布尔)作为JSON字符串的参数的JSON数组存储类名称(Range)。

# File ext/json/lib/json/add/range.rb, line 26 def to_json(*args) as_json.to_json(*args) end

to_s → string Show source

将此范围对象转换为可打印的形式(使用to_s来转换开始和结束对象)。

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