Spec-Zone.ru › Ruby 3.3

класс Range

Родитель:
Объект
Включенные модули:
Enumerable

Объект 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:

# Ranges that by default include the given end value.
Range.new(1, 4).to_a     # => [1, 2, 3, 4]
Range.new('a', 'd').to_a # => ["a", "b", "c", "d"]
# Ranges that use third argument +exclude_end+ to exclude the given end value.
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]
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, которые это делают, включают Array, Complex, File::Stat, Float, Integer, Kernel, Module, Numeric, Rational, String, Symbol и Time.

Пример:

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, которые это делают, включают Integer, String и Symbol (но не другие классы, упомянутые выше).

Методы итератора включают:

  • В самом Range: each, step и %

  • Включенные из модуля Enumerable: each_entry, each_with_index, each_with_object, each_slice, each_cons и reverse_each.

Пример:

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:

  • Наследуется от класса Объект.

  • Включает модуль Enumerable, который предоставляет десятки дополнительных методов.

Здесь класс Range предоставляет методы, полезные для:

  • Создание диапазона

  • Запросы

  • Сравнение

  • Итерация

  • Преобразование

  • Методы работы с JSON

Методы создания диапазона

  • ::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; вызывает блок с каждым n-м элементом self.

  • each: Вызывает блок с каждым элементом self.

  • step: Принимает необязательный аргумент n (по умолчанию 1); вызывает блок с каждым n-м элементом self.

Методы преобразования

  • inspect: Возвращает строковое представление self (использует inspect).

  • to_a (алиас entries): Возвращает элементы self в массиве.

  • to_s: Возвращает строковое представление self (использует to_s).

Методы работы с JSON

  • ::json_create: Возвращает новый объект Range, построенный из данного объекта.

  • as_json: Возвращает 2-элементный хэш, представляющий self.

  • to_json: Возвращает JSON-строку, представляющую self.

Для того, чтобы эти методы были доступны:

require 'json/add/range'
END_OF_DOCUMENT_MARKER

Методы публичного класса

json_create(object) Показать исходный код
# File ext/json/lib/json/add/range.rb, line 9
def self.json_create(object)
  new(*object['a'])
end

См. as_json.

new(begin, end, exclude_end = false) → new_range Показать исходный код
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;
}

Возвращает новый диапазон на основе заданных объектов begin и end. Необязательный аргумент 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"]

Методы экземпляра открытого доступа

%(n) {|element| ... } → self Показать исходный код
%(n) → enumerator
static VALUE
range_percent_step(VALUE range, VALUE step)
{
    return range_step(1, &step, range);
}

Итерируется по элементам self.

При наличии блока, вызывает блок с выбранными элементами диапазона; возвращает self:

a = []
(1..5).%(2) {|element| a.push(element) } # => 1..5
a # => [1, 3, 5]
a = []
('a'..'e').%(2) {|element| a.push(element) } # => "a".."e"
a # => ["a", "c", "e"]

Без блока возвращает перечислитель, который будет класса Enumerator::ArithmeticSequence, если self числовой; в противном случае класса Enumerator:

e = (1..5) % 2 # => ((1..5).%(2))
e.class        # => Enumerator::ArithmeticSequence
('a'..'e') % 2 # =>  #<Enumerator: ...>

Связанно с: Range#step.

self == other → true or 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);
}

Возвращает 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?.

self === object → true or false Показать исходный код
static VALUE
range_eqq(VALUE range, VALUE val)
{
    return r_cover_p(range, RANGE_BEG(range), RANGE_END(range), val);
}

Возвращает true если object находится между self.begin и self.end. 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"
as_json(*) Показать исходный код
# File ext/json/lib/json/add/range.rb, line 31
def as_json(*)
  {
    JSON.create_id  => self.class.name,
    'a'             => [ first, last, exclude_end? ]
  }
end

Методы Range#as_json и Range.json_create могут использоваться для сериализации и десериализации объекта Range; см. Marshal.

Метод Range#as_json сериализует self, возвращая хеш из 2 элементов, представляющий self:

require 'json/add/range'
x = (1..4).as_json     # => {"json_class"=>"Range", "a"=>[1, 4, false]}
y = (1...4).as_json    # => {"json_class"=>"Range", "a"=>[1, 4, true]}
z = ('a'..'d').as_json # => {"json_class"=>"Range", "a"=>["a", "d", false]}

Метод JSON.create десериализует такой хеш, возвращая объект Range:

Range.json_create(x) # => 1..4
Range.json_create(y) # => 1...4
Range.json_create(z) # => "a".."d"
begin → object Показать исходный код
static VALUE
range_begin(VALUE range)
{
    return RANGE_BEG(range);
}

Возвращает объект, определяющий начало self.

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

Связанно с: Range#first, Range#end.

bsearch {|obj| block } → value Показать исходный код
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;
}

Возвращает элемент из self, выбранный с помощью двоичного поиска.

См. Двоичный поиск.

count → integer
count(object) → integer
count {|element| ... } → integer
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);
}

Возвращает количество элементов, в зависимости от заданного аргумента или блока.

Без аргумента и блока возвращает число элементов:

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

С аргументом object, возвращает количество object в self, обычно ноль или один:

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

С блоком вызывает блок с каждым элементом; возвращает количество элементов, для которых блок возвращает истинное значение:

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

Связанно с: Range#size.

cover?(object) → true or false
cover?(range) → true or false
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);
}

Возвращает true если заданный аргумент находится в self, false в противном случае.

Для аргумента, не являющегося диапазоном, object, оценивает с <= и <.

Для диапазона self с включенным конечным значением (#exclude_end? == false):

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

Для диапазона r с исключённым конечным значением (#exclude_end? == true):

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, сравнивает первые и последние элементы self и range:

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

Если begin и end числовые, 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?.

each {|element| ... } → self
each → an_enumerator
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;
}

При наличии блока, передает каждый элемент self в блок:

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

Вызывает исключение, если self.first.respond_to?(:succ).

Без блока возвращает перечислитель.

end → object Показать исходный код
static VALUE
range_end(VALUE range)
{
    return RANGE_END(range);
}

Возвращает объект, определяющий конец self.

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

Связанно с: Range#begin, Range#last.

entries
Псевдоним для: to_a
eql?(other) → true or 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);
}

Возвращает 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#==.

exclude_end? → true or false Показать исходный код
static VALUE
range_exclude_end_p(VALUE range)
{
    return RBOOL(EXCL(range));
}

Возвращает true если self исключает своё конечное значение; 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
END_OF_DOCUMENT_MARKER
first → объект Показать исходный код
first(n) → массив
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];
}

Без аргумента возвращает первый элемент 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
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);
}

Возвращает целое число-хеш значение для self. Два объекта диапазона r0 и r1 имеют одинаковое значение хеша тогда и только тогда, когда r0.eql?(r1).

Связанные: Range#eql?, Object#hash.

include?(объект) → true или false

Возвращает true если object является элементом self, 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

Если begin и end числовые, 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 → строка Показать исходный код
static VALUE
range_inspect(VALUE range)
{
    return rb_exec_recursive(inspect_range, range, 0);
}

Возвращает строковое представление self, включая begin.inspect и end.inspect:

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

Обратите внимание, что возвращаемые значения to_s и inspect могут отличаться:

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

Связанные: Range#to_s.

last → объект Показать исходный код
last(n) → массив
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));
}

Без аргумента возвращает последний элемент self, если он существует:

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

Обратите внимание, что last без аргумента возвращает конечный элемент self даже если exclude_end? true:

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

Обратите внимание, что last с аргументом не возвращает конечный элемент self если exclude_end? это true:

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

Вызывает исключение, если последнего элемента нет:

(1..).last # Raises RangeError
max → объект Показать исходный код
max(n) → массив
max {|a, b| ... } → объект
max(n) {|a, b| ... } → массив
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;
    }
}

Возвращает максимальное значение в self, используя метод <=> или заданный блок для сравнения.

Без аргумента и без блока возвращает элемент с максимальным значением в self.

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

С целочисленным аргументом n и без блока возвращает n элементов с максимальным значением из self в виде массива:

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

member?
Также является псевдонимом для: include?
min → объект Показать исходный код
min(n) → массив
min {|a, b| ... } → объект
min(n) {|a, b| ... } → массив
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;
    }
}

Возвращает минимальное значение в self, используя метод <=> или заданный блок для сравнения.

Без аргумента и без блока возвращает элемент с минимальным значением в self.

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

С целочисленным аргументом n и без блока возвращает n элементов с минимальным значением из self в виде массива:

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

minmax → [объект, объект] Показать исходный код
minmax {|a, b| ... } → [объект, объект]
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)
    );
}

Возвращает массив из 2 элементов, содержащий минимальное и максимальное значение в self, либо в соответствии с методом сравнения <=>, либо в соответствии с заданным блоком.

Без блока возвращает минимальное и максимальное значения, используя <=> для сравнения:

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

При использовании блока, блок должен возвращать целое число:

  • Отрицательное, если a меньше, чем b.

  • Ноль, если a и b равны.

  • Положительное, если a больше, чем b.

Блок вызывается self.size раз для сравнения элементов; возвращает массив из 2 элементов Array, содержащий минимальное и максимальное значения из self, в соответствии с блоком:

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

overlap?(range) → true или false Показать исходный код
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 both begin values are equal, no more comparisons needed */
    if (rb_equal(self_beg, other_beg)) return Qtrue;

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

Возвращает true если range перекрывается с self, иначе false.

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

При недиапазонном аргументе, выбрасывает TypeError.

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

Возвращает false если внутренний вызов <=> возвращает nil; то есть операнды не сравнимы.

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

Возвращает false если self или range пустой. «Пустой диапазон» означает, что его начальное значение больше или равно для исключающего диапазона, его конечному значению.

(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

Возвращает false если начальное значение одного из self и range больше или равно, если другое — исключающий диапазон, конечному значению другого.

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

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

Возвращает false если конечное значение одного из self и range больше или равно, если другое — исключающий диапазон, конечному значению другого.

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

reverse_each {|элемент| ... } → self Показать исходный код
reverse_each → итератор
static VALUE
range_reverse_each(VALUE range)
{
    RETURN_SIZED_ENUMERATOR(range, 0, 0, range_enum_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;
}

При наличии блока, передаёт каждый элемент 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]

Без блока возвращает итератор.

size → неотрицательное целое число или Бесконечность или nil Показать исходный код
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)) {
        if (rb_obj_is_kind_of(e, rb_cNumeric)) {
            return DBL2NUM(HUGE_VAL);
        }
    }

    return Qnil;
}

Возвращает количество элементов в self если начальное и конечное значения числовые; иначе возвращает nil:

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

Связанные: Range#count.

step(n = 1) {|элемент| ... } → self Показать исходный код
step(n = 1) → итератор
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()) {
        if (!rb_obj_is_kind_of(step, rb_cNumeric)) {
            step = rb_to_int(step);
        }
        if (rb_equal(step, INT2FIX(0))) {
            rb_raise(rb_eArgError, "step can't be 0");
        }

        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);
    VALUE iter[2] = {INT2FIX(1), 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 */
        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)) {
            b = tmp;
            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 {
            if (!discrete_object_p(b)) {
                rb_raise(rb_eTypeError, "can't iterate from %s",
                         rb_obj_classname(b));
            }
            if (!NIL_P(e))
                range_each_func(range, step_i, (VALUE)iter);
            else
                for (;; b = rb_funcallv(b, id_succ, 0, 0))
                    step_i(b, (VALUE)iter);
        }
    }
    return range;
}

Итерирует по элементам self.

С блоком и без аргумента, вызывает блок для каждого элемента диапазона; возвращает self:

a = []
(1..5).step {|element| a.push(element) } # => 1..5
a # => [1, 2, 3, 4, 5]
a = []
('a'..'e').step {|element| a.push(element) } # => "a".."e"
a # => ["a", "b", "c", "d", "e"]

С блоком и положительным целым аргументом n вызывает блок с элементом 0, элементом n, элементом 2n, и так далее:

a = []
(1..5).step(2) {|element| a.push(element) } # => 1..5
a # => [1, 3, 5]
a = []
('a'..'e').step(2) {|element| a.push(element) } # => "a".."e"
a # => ["a", "c", "e"]

Без блока возвращает итератор, который будет класса Enumerator::ArithmeticSequence если self числовой; иначе класса Enumerator:

e = (1..5).step(2) # => ((1..5).step(2))
e.class            # => Enumerator::ArithmeticSequence
('a'..'e').step # => #<Enumerator: ...>

Связанные: Range#%.

to_a → массив Показать исходный код
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);
}

Возвращает массив, содержащий элементы self, если это конечная коллекция; в противном случае выбрасывает исключение.

(1..4).to_a     # => [1, 2, 3, 4]
(1...4).to_a    # => [1, 2, 3]
('a'..'d').to_a # => ["a", "b", "c", "d"]
Также алиасируется как: entries
to_json(*args) Показать исходный код
# File ext/json/lib/json/add/range.rb, line 51
def to_json(*args)
  as_json.to_json(*args)
end

Возвращает строку JSON, представляющую self:

require 'json/add/range'
puts (1..4).to_json
puts (1...4).to_json
puts ('a'..'d').to_json

Вывод:

{"json_class":"Range","a":[1,4,false]}
{"json_class":"Range","a":[1,4,true]}
{"json_class":"Range","a":["a","d",false]}
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);

    return str;
}

Возвращает строковое представление self, включая begin.to_s и end.to_s:

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

Обратите внимание, что возвращаемые значения из to_s и inspect могут отличаться:

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

Связанные: Range#inspect.

Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API