Spec-Zone.ru › Ruby 3.1

класс Range

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

Объект Range представляет собой коллекцию значений, которые находятся между заданными начальным и конечным значениями.

Вы можете создать объект Range явно с помощью:

  • Литтерала Range:

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

Range также может быть создан с помощью метода 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]

Интервалы и другие классы

Объект может быть помещён в интервал, если его класс реализует метод экземпляра <=>. В ядре 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 предоставляет методы, которые полезны для:

  • Создание интервала

  • Запросы

  • Сравнения

  • Итерация

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

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

  • ::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 с аргументами, сериализованными с помощью 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_include_internal(range, val, 1);
    if (ret != Qundef) 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

Если 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; то есть операнды не могут быть сравнены.

Связанно с: 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 → integer Показать исходный код
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?(object) → 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 {
        struct cmp_opt_data cmp_opt = { 0, 0 };
        int c = NIL_P(b) ? -1 : OPTIMIZED_CMP(b, e, cmp_opt);

        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?(object) -> true или false Показать исходный код
static VALUE
range_include(VALUE range, VALUE val)
{
    VALUE ret = range_include_internal(range, val, 0);
    if (ret != Qundef) 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 {
        struct cmp_opt_data cmp_opt = { 0, 0 };
        VALUE b = RANGE_BEG(range);
        VALUE e = RANGE_END(range);
        int c = NIL_P(e) ? -1 : OPTIMIZED_CMP(b, e, cmp_opt);

        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.

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)) {
        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) → перечислитель
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 → массив Показать исходный код
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 → строка Показать исходный код
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