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
序列化参数的新Ra
nge对象来反序列化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数组存储类名称(Ra
nge)。
# 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;
}