类 Range

Range 对象表示给定起始值和结束值之间的值的集合。

您可以使用以下方式显式创建一个 Range 对象:

  • 范围字面量

    # Ranges that use '..' to include the given end value.
    (1..4).to_a      # => [1, 2, 3, 4]
    ('a'..'d').to_a  # => ["a", "b", "c", "d"]
    # Ranges that use '...' to exclude the given end value.
    (1...4).to_a     # => [1, 2, 3]
    ('a'...'d').to_a # => ["a", "b", "c"]
    
  • 方法 Range.new

    # 默认包含给定结束值的范围。Range.new(1, 4).to_a # => [1, 2, 3, 4] Range.new('a', 'd').to_a # => ["a", "b", "c", "d"] # 使用第三个参数 exclude_end 排除给定结束值的范围。Range.new(1, 4, true).to_a # => [1, 2, 3] Range.new('a', 'd', true).to_a # => ["a", "b", "c"]

无始范围

一个无始范围具有确定的结束值,但起始值为 nil。这样的范围包含所有直到结束值的值。

r = (..4)               # => nil..4
r.begin                 # => nil
r.include?(-50)         # => true
r.include?(4)           # => true

r = (...4)              # => nil...4
r.include?(4)           # => false

Range.new(nil, 4)       # => nil..4
Range.new(nil, 4, true) # => nil...4

无始范围可用于切片数组

a = [1, 2, 3, 4]
# Include the third array element in the slice
r = (..2)  # => nil..2
a[r]       # => [1, 2, 3]
# Exclude the third array element from the slice
r = (...2) # => nil...2
a[r]       # => [1, 2]

无始范围的 each 方法会引发异常。

无终范围

一个无终范围具有确定的起始值,但结束值为 nil。这样的范围包含从起始值开始的所有值。

r = (1..)         # => 1..
r.end             # => nil
r.include?(50)    # => true

Range.new(1, nil) # => 1..

无终范围的字面量可以用两个点或三个点表示。无论哪种方式,范围都具有相同的元素。但请注意,两者不相等。

r0 = (1..)           # => 1..
r1 = (1...)          # => 1...
r0.begin == r1.begin # => true
r0.end == r1.end     # => true
r0 == r1             # => false

无终范围可用于切片数组

a = [1, 2, 3, 4]
r = (2..) # => 2..
a[r]      # => [3, 4]

无终范围的 each 方法会无限期地调用给定的块。

a = []
r = (1..)
r.each do |i|
  a.push(i) if i.even?
  break if i > 10
end
a # => [2, 4, 6, 8, 10]

一个范围可以既无始又无终。对于字面量的无始无终范围,至少范围的开头或结尾必须作为显式的 nil 值给出。建议使用显式的 nil 开头和隐式的 nil 结尾,因为这是 Ruby 用于 Range#inspect 的方式。

(nil..)    # => (nil..)
(..nil)    # => (nil..)
(nil..nil) # => (nil..)

范围与其他类

如果一个对象的类实现了实例方法 #<=>,则该对象可以放入范围中。实现此方法的 Ruby 核心类包括 ArrayComplexFile::StatFloatIntegerKernelModuleNumericRationalStringSymbolTime

示例

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

仅当范围的元素实现了实例方法 succ 时,才能对范围进行迭代。实现此方法的 Ruby 核心类包括 IntegerStringSymbol(但不包括上面提到的其他类)。

迭代器方法包括:

示例

a = []
(1..4).each {|i| a.push(i) }
a # => [1, 2, 3, 4]

范围和用户自定义类

要在范围中使用的用户自定义类必须实现实例方法 #<=>;请参阅 Integer#<=>。要使迭代可用,它还必须实现实例方法 succ;请参阅 Integer#succ

下面的类同时实现了 #<=>succ,因此既可用于构造范围,也可用于迭代范围。请注意,包含了 Comparable 模块,因此 == 方法是根据 #<=> 定义的。

# 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

这里有什么

首先,看看其他地方有什么。类 Range:

在这里,类 Range 提供了以下有用的方法:

创建范围的方法

  • ::new:返回一个新的范围。

查询方法

  • begin:返回为 self 给定的起始值。

  • bsearch:返回由二分搜索从 self 中选择的元素。

  • count:返回 self 中元素的计数。

  • end:返回为 self 给定的结束值。

  • exclude_end?:返回是否排除结束对象。

  • first:返回 self 的第一个元素。

  • hash:返回整数哈希码。

  • last:返回 self 的最后一个元素。

  • max:返回 self 中的最大值。

  • min:返回 self 中的最小值。

  • minmax:返回 self 中的最小值和最大值。

  • size:返回 self 中元素的计数。

比较方法

  • ==:返回给定对象是否等于 self(使用 ==)。

  • ===:返回给定对象是否在起始值和结束值之间。

  • cover?:返回给定对象是否在 self 中。

  • eql?:返回给定对象是否等于 self(使用 eql?)。

  • include?(别名为 member?):返回给定对象是否为 self 的元素。

迭代方法

  • %:需要参数 n;使用 self 的每个第 n 个元素调用块。

  • each:使用 self 的每个元素调用块。

  • step:接受可选参数 n(默认为 1);使用 self 的每个第 n 个元素调用块。

转换方法

  • inspect:返回 self 的字符串表示形式(使用 inspect)。

  • to_a(别名为 entries):返回数组中 self 的元素。

  • to_s:返回 self 的字符串表示形式(使用 to_s)。

处理 JSON 的方法

  • ::json_create:返回从给定对象构造的新 Range 对象。

  • as_json:返回表示 self 的 2 元素哈希。

  • to_json:返回表示 self 的 JSON 字符串。

要使这些方法可用:

require 'json/add/range'

公共类方法

new(begin, end, exclude_end = false) → new_range 点击以切换源

返回基于给定对象 beginend 的新范围。可选参数 exclude_end 确定对象 end 是否作为范围中的最后一个对象包含。

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

公共实例方法

%(n) {|element| ... } → self 点击以切换源
%(n) → enumerator 或 arithmetic_sequence

step 相同(但未提供 n 的默认值)。此方法对于表达性地生成 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]

请注意,由于 Ruby 中的运算符优先级,在这种情况下,范围周围必须使用括号

(0..7) % 2 #=> ((0..7).%(2)) -- as expected
0..7 % 2 #=> 0..1 -- parsed as 0..(7 % 2)
static VALUE
range_percent_step(VALUE range, VALUE step)
{
    return range_step(1, &step, range);
}
self == other → true 或 false 点击以切换源

当且仅当满足以下条件时,返回 true

  • other 是一个范围。

  • other.begin == self.begin.

  • other.end == self.end.

  • other.exclude_end? == self.exclude_end?.

否则返回 false

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

请注意,即使使用相同的参数,==eql? 的返回值也可能不同。

(1..2) == (1..2.0)   # => true
(1..2).eql? (1..2.0) # => false

相关:Range#eql?

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);
}
self === object → true 或 false 点击以切换源

如果 objectself.beginself.end 之间,则返回 true。否则返回 false

(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

case 语句使用方法 ===,因此:

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"
static VALUE
range_eqq(VALUE range, VALUE val)
{
    return r_cover_p(range, RANGE_BEG(range), RANGE_END(range), val);
}
begin → object 点击以切换源

返回定义 self 开头的对象。

(1..4).begin # => 1
(..2).begin  # => nil

相关: Range#first, Range#end.

static VALUE
range_begin(VALUE range)
{
    return RANGE_BEG(range);
}
bsearch {|obj| block } → value 点击切换源码

返回通过二分查找从 self 中选择的元素。

请参阅 二分查找

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;
}
count → integer 点击切换源码
count(object) → integer
count {|element| ... } → integer

返回基于给定参数或代码块条件(如果给定)的元素计数。

如果没有给定参数和代码块,则返回元素的数量。

(1..4).count      # => 4
(1...4).count     # => 3
('a'..'d').count  # => 4
('a'...'d').count # => 3
(1..).count       # => Infinity
(..4).count       # => Infinity

如果给定参数 object,则返回在 self 中找到的 object 的数量,通常为零或一。

(1..4).count(2)   # => 1
(1..4).count(5)   # => 0
(1..4).count('a')  # => 0

如果给定代码块,则为每个元素调用该代码块;返回代码块返回真值的元素的数量。

(1..4).count {|element| element < 3 } # => 2

相关: Range#size.

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);
}
cover?(object) → true or false 点击切换源码
cover?(range) → true or false

如果给定参数在 self 范围内,则返回 true,否则返回 false

对于非范围参数 object,使用 <=< 进行评估。

对于包含结束值(#exclude_end? == false)的范围 self,按如下方式评估:

self.begin <= object <= self.end

示例

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

对于排除结束值(#exclude_end? == true)的范围 r,按如下方式评估:

r.begin <= object < r.end

示例

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

对于范围参数 range,比较 selfrange 的第一个和最后一个元素

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

如果起始和结束是数值型的,cover? 的行为类似于 include?

(1..3).cover?(1.5) # => true
(1..3).include?(1.5) # => true

但当不是数值型时,这两个方法可能会有所不同

('a'..'d').cover?('cc')   # => true
('a'..'d').include?('cc') # => false

如果以下任一情况成立,则返回 false

  • self 的起始值大于其结束值。

  • #<=> 的内部调用返回 nil;也就是说,操作数不可比较。

无起始范围覆盖结束之前的所有相同类型的值,排除独占范围的结束值。无起始范围覆盖在无起始范围结束之前的范围,或者对于包含范围,覆盖到无起始范围的末尾。

(..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

无结束范围覆盖起始之后的所有相同类型的值。无结束的独占范围不覆盖无结束的包含范围。

(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

既无起始也无结束的范围覆盖所有值和范围,并对所有参数返回 true,例外情况是无起始和无结束的独占范围不覆盖无结束的包含范围。

(nil...).cover?(Object.new) # => true
(nil...).cover?(nil...)     # => true
(nil..).cover?(nil...)      # => true
(nil...).cover?(nil..)      # => false
(nil...).cover?(1..)        # => false

相关: Range#include?.

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 {|element| ... } → self 点击切换源码
each → an_enumerator

如果给定代码块,则将 self 的每个元素传递给该代码块

a = []
(1..4).each {|element| a.push(element) } # => 1..4
a # => [1, 2, 3, 4]

除非 self.first.respond_to?(:succ),否则引发异常。

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

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;
}
end → object 点击切换源码

返回定义 self 结尾的对象。

(1..4).end  # => 4
(1...4).end # => 4
(1..).end   # => nil

相关: Range#begin, Range#last.

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

返回一个包含 self 中元素的数组(如果是一个有限集合);否则会引发异常。

(1..4).to_a     # => [1, 2, 3, 4]
(1...4).to_a    # => [1, 2, 3]
('a'..'d').to_a # => ["a", "b", "c", "d"]
别名: to_a
eql?(other) → true or false 点击切换源码

当且仅当满足以下条件时,返回 true

  • other 是一个范围。

  • other.begin.eql?(self.begin).

  • other.end.eql?(self.end).

  • other.exclude_end? == self.exclude_end?.

否则返回 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

请注意,即使使用相同的参数,==eql? 的返回值也可能不同。

(1..2) == (1..2.0)   # => true
(1..2).eql? (1..2.0) # => false

相关: Range#==.

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 点击切换源码

如果 self 排除其结束值,则返回 true;否则返回 false

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
static VALUE
range_exclude_end_p(VALUE range)
{
    return RBOOL(EXCL(range));
}
first → object 点击切换源码
first(n) → array

如果没有参数,则返回 self 的第一个元素(如果存在)

(1..4).first     # => 1
('a'..'d').first # => "a"

如果给定非负整数参数 n,则返回数组中的前 n 个元素

(1..10).first(3) # => [1, 2, 3]
(1..10).first(0) # => []
(1..4).first(50) # => [1, 2, 3, 4]

如果不存在第一个元素,则引发异常

(..4).first # Raises RangeError
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 点击切换源码

返回 self 的整数哈希值。当且仅当 r0.eql?(r1) 时,两个范围对象 r0r1 具有相同的哈希值。

相关: Range#eql?, 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 ST2FIX(hash);
}
include?(object) → true or false

如果 objectself 的一个元素,则返回 true,否则返回 false

(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

如果起始和结束是数值型的,include? 的行为类似于 cover?

(1..3).include?(1.5) # => true
(1..3).cover?(1.5) # => true

但当不是数值型时,这两个方法可能会有所不同

('a'..'d').include?('cc') # => false
('a'..'d').cover?('cc')   # => true

相关: Range#cover?.

别名: member?
inspect → string 点击切换源码

返回 self 的字符串表示形式,包括 begin.inspectend.inspect

(1..4).inspect  # => "1..4"
(1...4).inspect # => "1...4"
(1..).inspect   # => "1.."
(..4).inspect   # => "..4"

请注意,从 to_sinspect 返回的值可能不同

('a'..'d').to_s    # => "a..d"
('a'..'d').inspect # => "\"a\"..\"d\""

相关: Range#to_s.

static VALUE
range_inspect(VALUE range)
{
    return rb_exec_recursive(inspect_range, range, 0);
}
last → object 点击切换源码
last(n) → array

如果没有参数,则返回 self 的最后一个元素(如果存在)

(1..4).last     # => 4
('a'..'d').last # => "d"

请注意,即使 exclude_end?true,没有参数的 last 也会返回 self 的结束元素

(1...4).last     # => 4
('a'...'d').last # => "d"

如果给定非负整数参数 n,则返回数组中的最后 n 个元素

(1..10).last(3) # => [8, 9, 10]
(1..10).last(0) # => []
(1..4).last(50) # => [1, 2, 3, 4]

请注意,如果 exclude_end?true,则带参数的 last 不会返回 self 的结束元素

(1...4).last(3)     # => [1, 2, 3]
('a'...'d').last(3) # => ["a", "b", "c"]

如果不存在最后一个元素,则引发异常

(1..).last # Raises RangeError
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 → object 点击切换源码
max(n) → array
max {|a, b| ... } → object
max(n) {|a, b| ... } → array

返回 self 中的最大值,使用方法 #<=> 或给定的代码块进行比较。

如果没有给定参数和代码块,则返回 self 的最大值元素。

(1..4).max     # => 4
('a'..'d').max # => "d"
(-4..-1).max   # => -1

如果给定非负整数参数 n,并且没有给定代码块,则返回数组中 selfn 个最大值元素

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

如果给定代码块,则会调用它

  • 首先,使用 self 的前两个元素。

  • 然后,按顺序,使用到目前为止的最大值和 self 的下一个元素。

为了说明

(1..4).max {|a, b| p [a, b]; a <=> b } # => 4

输出

[2, 1]
[3, 2]
[4, 3]

如果没有给定参数和代码块,则返回对代码块的最后一次调用的返回值

(1..4).max {|a, b| -(a <=> b) } # => 1

如果给定非负整数参数 n 和代码块,则返回数组中对代码块的最后 n 次调用的返回值

(1..4).max(2) {|a, b| -(a <=> b) }  # => [1, 2]
(1..4).max(50) {|a, b| -(a <=> b) } # => [1, 2, 3, 4]

如果 n 为零,则返回一个空数组

(1..4).max(0)                      # => []
(1..4).max(0) {|a, b| -(a <=> b) } # => []

如果符合以下条件,则返回 nil 或空数组:

  • 范围的起始值大于结束值

    (4..1).max                         # => nil
    (4..1).max(2)                      # => []
    (4..1).max {|a, b| -(a <=> b) }    # => nil
    (4..1).max(2) {|a, b| -(a <=> b) } # => []
    
  • 独占范围的起始值等于结束值

    (1...1).max                          # => nil
    (1...1).max(2)                       # => []
    (1...1).max  {|a, b| -(a <=> b) }    # => nil
    (1...1).max(2)  {|a, b| -(a <=> b) } # => []
    

如果以下任一情况成立,则引发异常:

  • self 是一个无结束范围:(1..)

  • 给定了代码块,并且 self 是一个无起始范围。

相关: Range#min, Range#minmax.

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) || argc) {
        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 {
        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 (!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?(object) -> true or false 点击切换源码

如果 objectself 的一个元素,则返回 true,否则返回 false

(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

如果起始和结束是数值型的,include? 的行为类似于 cover?

(1..3).include?(1.5) # => true
(1..3).cover?(1.5) # => true

但当不是数值型时,这两个方法可能会有所不同

('a'..'d').include?('cc') # => false
('a'..'d').cover?('cc')   # => true

相关: Range#cover?.

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);
}
别名: include?
min → object 点击切换源码
min(n) → array
min {|a, b| ... } → object
min(n) {|a, b| ... } → array

返回 self 中的最小值,使用方法 #<=> 或给定的代码块进行比较。

如果没有给定参数和代码块,则返回 self 的最小值元素。

(1..4).min     # => 1
('a'..'d').min # => "a"
(-4..-1).min   # => -4

如果给定非负整数参数 n,并且没有给定代码块,则返回数组中 selfn 个最小值元素

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

如果给定代码块,则会调用它

  • 首先,使用 self 的前两个元素。

  • 然后,按顺序,使用到目前为止的最小值和 self 的下一个元素。

为了说明

(1..4).min {|a, b| p [a, b]; a <=> b } # => 1

输出

[2, 1]
[3, 1]
[4, 1]

如果没有给定参数和代码块,则返回对代码块的最后一次调用的返回值

(1..4).min {|a, b| -(a <=> b) } # => 4

如果给定非负整数参数 n 和代码块,则返回数组中对代码块的最后 n 次调用的返回值

(1..4).min(2) {|a, b| -(a <=> b) }  # => [4, 3]
(1..4).min(50) {|a, b| -(a <=> b) } # => [4, 3, 2, 1]

如果 n 为零,则返回一个空数组

(1..4).min(0)                      # => []
(1..4).min(0) {|a, b| -(a <=> b) } # => []

如果符合以下条件,则返回 nil 或空数组:

  • 范围的起始值大于结束值

    (4..1).min                         # => nil
    (4..1).min(2)                      # => []
    (4..1).min {|a, b| -(a <=> b) }    # => nil
    (4..1).min(2) {|a, b| -(a <=> b) } # => []
    
  • 独占范围的起始值等于结束值

    (1...1).min                          # => nil
    (1...1).min(2)                       # => []
    (1...1).min  {|a, b| -(a <=> b) }    # => nil
    (1...1).min(2)  {|a, b| -(a <=> b) } # => []
    

如果以下任一情况成立,则引发异常:

  • self 是一个无起始范围:(..4)

  • 给定了代码块,并且 self 是一个无结束范围。

相关: Range#max, Range#minmax.

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;
    }
}
minmax → [object, object] 点击切换源码
minmax {|a, b| ... } → [object, object]

返回一个包含 self 中最小值和最大值的 2 个元素数组,可以根据比较方法 #<=> 或给定的代码块返回。

如果没有给定代码块,则返回最小值和最大值,使用 #<=> 进行比较

(1..4).minmax     # => [1, 4]
(1...4).minmax    # => [1, 3]
('a'..'d').minmax # => ["a", "d"]
(-4..-1).minmax   # => [-4, -1]

如果给定了代码块,则代码块必须返回一个整数

  • 如果 a 小于 b,则为负数。

  • 如果 ab 相等,则为零。

  • 如果 a 大于 b,则为正数。

调用代码块 self.size 次以比较元素;返回一个包含 self 中最小值和最大值的 2 个元素 Array,根据代码块

(1..4).minmax {|a, b| -(a <=> b) } # => [4, 1]

如果符合以下条件,则返回 [nil, nil]

  • 范围的起始值大于结束值

    (4..1).minmax                      # => [nil, nil]
    (4..1).minmax {|a, b| -(a <=> b) } # => [nil, nil]
    
  • 独占范围的起始值等于结束值

    (1...1).minmax                          # => [nil, nil]
    (1...1).minmax  {|a, b| -(a <=> b) }    # => [nil, nil]
    

如果 self 是无起始或无结束的范围,则引发异常。

相关: Range#min, Range#max.

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)
    );
}
overlap?(range) → true or false 点击切换源码

如果 rangeself 重叠,则返回 true,否则返回 false

(0..2).overlap?(1..3) #=> true
(0..2).overlap?(3..4) #=> false
(0..).overlap?(..0)   #=> true

对于非范围参数,会引发 TypeError

(1..3).overlap?(1)         # TypeError

如果对 #<=> 的内部调用返回 nil,则返回 false;也就是说,操作数不可比较。

(1..3).overlap?('a'..'d')  # => false

如果 selfrange 为空,则返回 false。“空范围”表示其起始值大于或等于(对于独占范围)其结束值。

(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

如果 selfrange 其中一个的起始值大于或等于(如果另一个是独占范围),则另一个的结束值,则返回 false

(4..5).overlap?(2..3)      # => false
(4..5).overlap?(2...4)     # => false

(1..2).overlap?(3..4)      # => false
(1...3).overlap?(3..4)     # => false

如果 selfrange 其中一个的结束值大于或等于(对于独占范围),则另一个的结束值,则返回 false

(4..5).overlap?(2..3)      # => false
(4..5).overlap?(2...4)     # => false

(1..2).overlap?(3..4)      # => false
(1...3).overlap?(3..4)     # => false

请注意,该方法不会对无起始范围是否实际为空做任何假设,即使其上限是其类型的最小值,因此所有这些都将返回 true

(...-Float::INFINITY).overlap?(...-Float::INFINITY) # => true
(..."").overlap?(..."") # => true
(...[]).overlap?(...[]) # => true

即使这些范围实际上是空的(没有数字可以小于 -Float::INFINITY),它们仍然被认为与自身重叠。

相关: Range#cover?.

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)) {
        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;
}
reverse_each {|element| ... } → self 点击切换源码
reverse_each → an_enumerator

如果给定代码块,则以相反的顺序将 self 的每个元素传递给该代码块

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]

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

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;
}
size → non_negative_integer or Infinity or nil 点击切换源码

如果起始值和结束值都是数值型的,则返回 self 中的元素计数;否则返回 nil

(1..4).size      # => 4
(1...4).size     # => 3
(1..).size       # => Infinity
('a'..'z').size  # => nil

如果 self 不可迭代,则会引发异常。

(0.5..2.5).size  # TypeError
(..1).size       # TypeError

相关:Range#count

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;
}
step(s = 1) {|element| ... } → self 点击切换源码
step(s = 1) → enumerator/arithmetic_sequence

s 为步长迭代范围内的元素。迭代通过 + 运算符执行。

(0..6).step(2) { puts _1 } #=> 1..5
# 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

如果 + step 减少了值,当步长 begin 高于 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

当未提供代码块,且范围边界和步长为 Numeric 类型时,该方法返回 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 可以进一步用作迭代或切片集合的值对象(请参见 Array#[])。有一个方便的方法 % ,其行为类似于 step,可以更清晰地生成算术序列。

# Same as (1..5).step(2)
(1..5) % 2 # => ((1..5).%(2))

在一般情况下,当未提供代码块时,会返回 Enumerator

('a'..).step('b')         #=> #<Enumerator: "a"..:step("b")>
('a'..).step('b').take(3) #=> ["a", "ab", "abb"]

如果未提供 s,则对于具有数字 begin 的范围,它被视为 1

(1..5).step { p _1 }
# Prints: 1, 2, 3, 4, 5

对于非数字范围,缺少步长是一个错误。

(Time.utc(2022, 3, 1)..Time.utc(2022, 2, 24)).step { p _1 }
# raises: step is required for non-numeric ranges (ArgumentError)

出于向后兼容的原因,String 范围支持使用字符串步长和整数步长进行迭代。在后一种情况下,通过使用 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
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);

    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 {
        v = b;
        if (!NIL_P(e)) {
            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 {
                // 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)
                dir = r_less(b, e);
                // 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 (r_less(b, rb_funcall(b, id_plus, 1, step)) == dir) {
                    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;
                        }
                    }
                }
            }
        }
        else
            for (;; v = rb_funcall(v, id_plus, 1, step))
                rb_yield(v);
    }
    return range;
}
to_a → array 点击切换源码

返回一个包含 self 中元素的数组(如果是一个有限集合);否则会引发异常。

(1..4).to_a     # => [1, 2, 3, 4]
(1...4).to_a    # => [1, 2, 3]
('a'..'d').to_a # => ["a", "b", "c", "d"]
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);
}
别名:entries
to_s → string 点击切换源码

返回 self 的字符串表示形式,包括 begin.to_send.to_s

(1..4).to_s  # => "1..4"
(1...4).to_s # => "1...4"
(1..).to_s   # => "1.."
(..4).to_s   # => "..4"

请注意,从 to_sinspect 返回的值可能不同

('a'..'d').to_s    # => "a..d"
('a'..'d').inspect # => "\"a\"..\"d\""

相关:Range#inspect

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