Spec-Zone.ru › Ruby 3.2

класс 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:

  • Наследуется от класса Object.

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

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

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

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

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

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

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

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

Публичные методы класса

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

Десериализует строку JSON, создавая новый объект Range с аргументами a, сериализованными методом to_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) → перечислитель
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 или 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 или false Показать исходный код
static VALUE
range_eqq(VALUE range, VALUE val)
{
    VALUE ret = range_string_cover_internal(range, val);
    if (!UNDEF_P(ret)) return ret;
    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 16
def as_json(*)
  {
    JSON.create_id  => self.class.name,
    'a'             => [ first, last, exclude_end? ]
  }
end

Возвращает хеш, который будет преобразован в объект JSON и представлять этот объект.

begin → объект Показать исходный код
static VALUE
range_begin(VALUE range)
{
    return RANGE_BEG(range);
}

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

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

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

bsearch {|obj| блок } → значение Показать исходный код
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(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_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, org_high;
        BSEARCH(int64_as_double_to_num);
    }
#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) {
                return bsearch_integer_range(beg, mid, 0);
            }
            diff = rb_funcall(diff, '*', 1, LONG2FIX(2));
        }
    }
    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) {
                return bsearch_integer_range(mid, end, 0);
            }
            diff = rb_funcall(diff, '*', 1, LONG2FIX(2));
        }
    }
    else {
        rb_raise(rb_eTypeError, "can't do binary search for %s", rb_obj_classname(beg));
    }
    return range;
}

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

См. Бинарный поиск.

count → целое число
count(объект) → целое число
count {|элемент| ... } → целое число
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);
    }
    else if (NIL_P(RANGE_END(range))) {
        /* We are confident that the answer is Infinity. */
        return DBL2NUM(HUGE_VAL);
    }
    else if (NIL_P(RANGE_BEG(range))) {
        /* We are confident that the answer is Infinity. */
        return DBL2NUM(HUGE_VAL);
    }
    else {
        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?(объект) → true или false
cover?(диапазон) → true или 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

Если начало и конец являются числовыми, 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 {|элемент| ... } → self
each → перечислитель
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 → объект Показать исходный код
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()

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

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

Range#entries является псевдонимом для Range#to_a.

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

Range#member? — псевдоним для Range#include?.

Псевдоним для: 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?(объект) -> true или false Show source
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);
}

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

Range#member? — псевдоним для Range#include?.

Также алиас: 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.

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

Возвращает массив из двух элементов, содержащий минимальное и максимальное значение в 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 раз для сравнения элементов; возвращает массив из двух элементов 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.

size → non_negative_integer or Infinity or 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) {|element| ... } → self Показать исходный код
step(n = 1) → enumerator
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));
            }
            range_each_func(range, step_i, (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 → array Показать исходный код
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"]

Range#entries является алиасом для Range#to_a.

Также алиасирован как: entries
to_json(*args) Показать исходный код
# File ext/json/lib/json/add/range.rb, line 26
def to_json(*args)
  as_json.to_json(*args)
end

Сохраняет имя класса (Range) с JSON массивом аргументов a, которые включают first (целое число), last (целое число) и exclude_end? (булево) как JSON строку.

to_s → string Показать исходный код
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