Spec-Zone.ru › Ruby 3

класс Range

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

Класс Range представляет интервал — множество значений с началом и концом. Диапазоны могут быть созданы с использованием литералов s..e и s...e или с помощью Range::new. Диапазоны, созданные с использованием .., охватывают значения от начала до конца включительно. Те, что созданы с использованием ..., не включают конечное значение. При использовании в качестве итератора диапазоны возвращают каждое значение в последовательности.

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

Диапазоны без начала/без конца

«Диапазон без начала» и «диапазон без конца» представляют полубесконечный диапазон. Литеральная запись для диапазона без начала:

(..1)
# or
(...1)

Литеральная запись для диапазона без конца:

(1..)
# or similarly
(1...)

Что эквивалентно

(1..nil)  # or similarly (1...nil)
Range.new(1, nil) # or Range.new(1, nil, true)

Диапазоны без начала/конца полезны, например, для идиоматического среза массивов:

[1, 2, 3, 4, 5][...2]   # => [1, 2]
[1, 2, 3, 4, 5][2...]   # => [3, 4, 5]

Некоторые детали реализации:

  • begin диапазона без начала и end диапазона без конца являются nil;

  • each диапазона без начала вызывает исключение;

  • each диапазона без конца перебирает бесконечную последовательность (может быть полезно в сочетании с Enumerable#take_while или аналогичными методами);

  • (1..) и (1...) не равны, хотя технически представляют одну и ту же последовательность.

Пользовательские объекты в диапазонах

Диапазоны могут быть созданы с использованием любых объектов, которые можно сравнивать с помощью оператора <=>. Методы, которые обрабатывают диапазон как последовательность (each и методы, унаследованные от Enumerable), ожидают, что объект начала будет реализовывать метод succ, чтобы возвращать следующий объект в последовательности. Методы step и include? требуют, чтобы начальный объект реализовывал succ или был числовым.

В классе Xs ниже реализованы как <=>, так и succ, чтобы Xs можно было использовать для создания диапазонов. Обратите внимание, что модуль Comparable включён, чтобы метод == был определён с точки зрения <=>.

class Xs                # represent a string of 'x's
  include Comparable
  attr :length
  def initialize(n)
    @length = n
  end
  def succ
    Xs.new(@length + 1)
  end
  def <=>(other)
    @length <=> other.length
  end
  def to_s
    sprintf "%2d #{inspect}", @length
  end
  def inspect
    'x' * @length
  end
end

Пример использования Xs для создания диапазона:

r = Xs.new(3)..Xs.new(6)   #=> xxx..xxxxxx
r.to_a                     #=> [xxx, xxxx, xxxxx, xxxxxx]
r.member?(Xs.new(5))       #=> true

Общедоступные методы класса

json_create(object) Показать исходный код
# 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) → rng Показать исходный код
static VALUE
range_initialize(int argc, VALUE *argv, VALUE range)
{
    VALUE beg, end, flags;

    rb_scan_args(argc, argv, "21", &beg, &end, &flags);
    range_modify(range);
    range_init(range, beg, end, RBOOL(RTEST(flags)));
    return Qnil;
}

Создаёт диапазон с заданным началом begin и концом end. Если параметр exclude_end опущен или равен false, диапазон будет включать конечный объект; в противном случае — исключать.

Общедоступные методы экземпляров

step(n=1) {| obj | блок } → rng Показать исходный код
step(n=1) → перечислитель
step(n=1) → арифметическая последовательность
rng % n → перечислитель
rng % n → арифметическая последовательность
static VALUE
range_percent_step(VALUE range, VALUE step)
{
    return range_step(1, &step, range);
}

Итерирует по диапазону, передавая каждый n-й элемент в блок. Если begin и end являются числовыми, n добавляется для каждой итерации. В противном случае step вызывает succ для итерации по элементам диапазона.

Если блок не задан, возвращается перечислитель. В частности, перечислитель является Enumerator::ArithmeticSequence, если begin и end диапазона являются числовыми.

range = Xs.new(1)..Xs.new(10)
range.step(2) {|x| puts x}
puts
range.step(3) {|x| puts x}

выводит:

 1 x
 3 xxx
 5 xxxxx
 7 xxxxxxx
 9 xxxxxxxxx

 1 x
 4 xxxx
 7 xxxxxxx
10 xxxxxxxxxx

См. Range для определения класса Xs.

rng == obj → 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 только если obj является Range, имеет эквивалентные элементы begin и end (сравнение с помощью ==) и имеет то же значение exclude_end?, что и диапазон.

(0..2) == (0..2)            #=> true
(0..2) == Range.new(0,2)    #=> true
(0..2) == (0...2)           #=> false
rng === obj → 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 если obj находится между begin и end диапазона, false в противном случае (так же, как cover?). Удобно, что === является оператором сравнения, используемым в case операторах.

case 79
when 1..50   then   puts "low"
when 51..75  then   puts "medium"
when 76..100 then   puts "high"
end
# Prints "high"

case "2.6.5"
when ..."2.4" then puts "EOL"
when "2.4"..."2.5" then puts "maintenance"
when "2.5"..."2.7" then puts "stable"
when "2.7".. then puts "upcoming"
end
# Prints "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 → obj Показать исходный код
static VALUE
range_begin(VALUE range)
{
    return RANGE_BEG(range);
}

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

(1..10).begin   #=> 1
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_TYPE_P(beg, T_FLOAT) || RB_TYPE_P(end, T_FLOAT)) {
        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;
}

Используя бинарный поиск, находит значение в диапазоне, которое удовлетворяет заданному условию за O(log n), где n — размер диапазона.

Вы можете использовать этот метод в двух случаях: режиме поиска минимума и режиме поиска любого значения. В обоих случаях элементы диапазона должны быть монотонными (или отсортированными) относительно блока.

В режиме поиска минимума (это хороший выбор для типичного случая использования) блок должен возвращать true или false, и должно существовать значение x, такое что:

  • блок возвращает false для любого значения, меньшего x, и

  • блок возвращает true для любого значения, большего или равного x.

Если x находится в диапазоне, этот метод возвращает значение x. В противном случае возвращает nil.

ary = [0, 4, 7, 10, 12]
(0...ary.size).bsearch {|i| ary[i] >= 4 } #=> 1
(0...ary.size).bsearch {|i| ary[i] >= 6 } #=> 2
(0...ary.size).bsearch {|i| ary[i] >= 8 } #=> 3
(0...ary.size).bsearch {|i| ary[i] >= 100 } #=> nil

(0.0...Float::INFINITY).bsearch {|x| Math.log(x) >= 0 } #=> 1.0

В режиме поиска любого значения (это соответствует bsearch(3) libc) блок должен возвращать число, и должны существовать два значения x и y (x <= y), такие что:

  • блок возвращает положительное число для v, если v < x,

  • блок возвращает ноль для v, если x <= v < y, и

  • блок возвращает отрицательное число для v, если y <= v.

Этот метод возвращает любое значение, которое находится в пересечении заданного диапазона и x…y (если таковое есть). Если нет значения, удовлетворяющего условию, возвращается nil.

ary = [0, 100, 100, 100, 200]
(0..4).bsearch {|i| 100 - ary[i] } #=> 1, 2 or 3
(0..4).bsearch {|i| 300 - ary[i] } #=> nil
(0..4).bsearch {|i|  50 - ary[i] } #=> nil

Вы не должны смешивать оба режима одновременно; блок всегда должен возвращать либо true/false, либо всегда возвращать число. Неопределено, какое значение фактически выбирается на каждой итерации.

count → int Показать исходный код
count(элемент) → int
count { |объект| блок } → int
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);
    }
}

Идентично Enumerable#count, за исключением того, что для бесконечных диапазонов возвращает Infinity.

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 если obj находится между началом и концом диапазона.

Это проверяет begin <= obj <= end когда exclude_end? равно false и begin <= obj < end когда exclude_end? равно true.

Если вызывается с аргументом Range, возвращает true когда заданный диапазон покрывается получателем, сравнивая значения begin и end. Если аргумент может рассматриваться как последовательность, этот метод рассматривает его таким образом. В конкретном случае (a..b).cover?(c...d) с a <= c && b < d, конец последовательности должен быть вычислен, что может привести к плохой производительности, если c не является числовым. Возвращает false если начальное значение диапазона больше конечного значения. Также возвращает false если одно из внутренних вызовов <=> возвращает nil (указывает, что объекты не сравнимы).

("a".."z").cover?("c")  #=> true
("a".."z").cover?("5")  #=> false
("a".."z").cover?("cc") #=> true
("a".."z").cover?(1)    #=> false
(1..5).cover?(2..3)     #=> true
(1..5).cover?(0..6)     #=> false
(1..5).cover?(1...6)    #=> true
each {| i | блок } → rng Показать исходный код
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;
}

Итерирует по элементам диапазона, передавая каждый из них в блок.

Метод each может быть использован только если начальный объект диапазона поддерживает метод succ. TypeError генерируется, если у объекта нет определенного метода succ (например, Float).

Если блок не задан, возвращается перечислитель.

(10..15).each {|n| print n, ' ' }
# prints: 10 11 12 13 14 15

(2.5..5).each {|n| print n, ' ' }
# raises: TypeError: can't iterate from Float
end → obj Показать исходный код
static VALUE
range_end(VALUE range)
{
    return RANGE_END(range);
}

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

(1..10).end    #=> 10
(1...10).end   #=> 10
entries → массив

Возвращает массив, содержащий элементы в диапазоне.

(1..7).to_a  #=> [1, 2, 3, 4, 5, 6, 7]
(1..).to_a   #=> RangeError: cannot convert endless range to an array
Псевдоним для: to_a
eql?(объект) → 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 только если obj является Range, имеет эквивалентные элементы begin и end (сравнение с помощью eql?) и имеет то же значение exclude_end?, что и диапазон.

(0..2).eql?(0..2)            #=> true
(0..2).eql?(Range.new(0,2))  #=> true
(0..2).eql?(0...2)           #=> false
exclude_end? → true или false Показать исходный код
static VALUE
range_exclude_end_p(VALUE range)
{
    return EXCL(range) ? Qtrue : Qfalse;
}

Возвращает true если диапазон исключает своё конечное значение.

(1..5).exclude_end?     #=> false
(1...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];
}

Возвращает первый объект в диапазоне или массив первых n элементов.

(10..20).first     #=> 10
(10..20).first(3)  #=> [10, 11, 12]
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);
}

Вычисляет хэш-код для данного диапазона. Два диапазона с одинаковыми начальными и конечными точками (используя eql?) и одинаковым значением exclude_end? будут генерировать одинаковый хэш-код.

См. также Object#hash.

include?(obj) → true или false

Возвращает true, если obj является элементом диапазона, false, в противном случае.

("a".."z").include?("g")   #=> true
("a".."z").include?("A")   #=> false
("a".."z").include?("cc")  #=> false

Если нужно убедиться, что obj находится между begin и end, используйте cover?.

("a".."z").cover?("cc")  #=> true

Если begin и end являются числовыми, include? ведет себя как cover?.

(1..3).include?(1.5) # => true
Псевдоним для: member?
inspect → строка Показать исходный код
static VALUE
range_inspect(VALUE range)
{
    return rb_exec_recursive(inspect_range, range, 0);
}

Преобразует этот объект диапазона в печатную форму (используя inspect для преобразования объектов begin и end).

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

Возвращает последний объект в диапазоне или массив из последних n элементов.

Обратите внимание, что без аргументов last вернёт объект, определяющий конец диапазона, даже если exclude_end? имеет значение true.

(10..20).last      #=> 20
(10...20).last     #=> 20
(10..20).last(3)   #=> [18, 19, 20]
(10...20).last(3)  #=> [17, 18, 19]
max → obj Показать исходный код
max {| a,b | блок } → obj
max(n) → obj
max(n) {| a,b | блок } → obj
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 является исключительным, не числовым диапазоном, вызывается Enumerable#max (через super) с аргументом и/или блоком для получения максимальных значений, если self — диапазон без начала, в этом случае возбуждается RangeError.

Если self — исключительный целочисленный диапазон (начало и конец диапазона — целые числа), и не заданы аргументы или блок, возвращает последнее значение в диапазоне (на один меньше, чем конец). В противном случае, если self — исключительный числовой диапазон, возбуждается TypeError.

Возвращает nil, если начальное значение диапазона больше конечного значения. Возвращает nil, если начальное значение исключительного диапазона равно конечному значению. Возбуждает RangeError, если вызов осуществляется для бесконечного диапазона.

Примеры:

(10..20).max                        #=> 20
(10..20).max(2)                     #=> [20, 19]
(10...20).max                       #=> 19
(10...20).max(2)                    #=> [19, 18]
(10...20).max{|x, y| -x <=> -y }    #=> 10
(10...20).max(2){|x, y| -x <=> -y } #=> [10, 11]
member?(obj) → 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, если obj является элементом диапазона, false, в противном случае.

("a".."z").include?("g")   #=> true
("a".."z").include?("A")   #=> false
("a".."z").include?("cc")  #=> false

Если нужно убедиться, что obj находится между begin и end, используйте cover?

("a".."z").cover?("cc")  #=> true

Если begin и end являются числовыми, include? ведет себя как cover?

(1..3).include?(1.5) # => true
Также алиасирован как: include?
min → obj Показать исходный код
min {| a,b | блок } → obj
min(n) → массив
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;
    }
}

Возвращает минимальное значение в диапазоне. Возвращает nil, если начальное значение диапазона больше конечного значения. Возвращает nil, если начальное значение исключительного диапазона равно конечному значению.

Можно указать необязательный блок для переопределения метода сравнения по умолчанию a <=> b.

(10..20).min    #=> 10
minmax → [obj, obj] Показать исходный код
minmax {| a,b | блок } → [obj, obj]
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)
    );
}

Возвращает массив из двух элементов, содержащий минимальное и максимальное значение в диапазоне.

Можно указать необязательный блок для переопределения метода сравнения по умолчанию a <=> b.

size → число Показать исходный код
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;
}

Возвращает количество элементов в диапазоне. И начало, и конец диапазона Range должны быть Numeric, в противном случае возвращается nil.

(10..20).size    #=> 11
('a'..'z').size  #=> nil
(-Float::INFINITY..Float::INFINITY).size #=> Infinity
step(n=1) {| obj | блок } → rng Показать исходный код
step(n=1) → перечислитель
step(n=1) → арифметическая последовательность
rng % n → перечислитель
rng % n → арифметическая последовательность
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);

    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 */
        VALUE iter[2];
        iter[0] = INT2FIX(1);
        iter[1] = step;

        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)) {
            VALUE iter[2];

            b = tmp;
            iter[0] = INT2FIX(1);
            iter[1] = step;

            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 {
            VALUE args[2];

            if (!discrete_object_p(b)) {
                rb_raise(rb_eTypeError, "can't iterate from %s",
                         rb_obj_classname(b));
            }
            args[0] = INT2FIX(1);
            args[1] = step;
            range_each_func(range, step_i, (VALUE)args);
        }
    }
    return range;
}

Итерируется по диапазону, передавая каждый n-ый элемент в блок. Если begin и end числовые, n добавляется на каждой итерации. В противном случае step вызывает succ для итерирования по элементам диапазона.

Если блок не задан, вместо него возвращается перечислитель. В частности, перечислитель является Enumerator::ArithmeticSequence, если начало и конец диапазона являются числовыми.

range = Xs.new(1)..Xs.new(10)
range.step(2) {|x| puts x}
puts
range.step(3) {|x| puts x}

дает:

 1 x
 3 xxx
 5 xxxxx
 7 xxxxxxx
 9 xxxxxxxxx

 1 x
 4 xxxx
 7 xxxxxxx
10 xxxxxxxxxx

См. Range для определения класса Xs.

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

Возвращает массив, содержащий элементы диапазона.

(1..7).to_a  #=> [1, 2, 3, 4, 5, 6, 7]
(1..).to_a   #=> RangeError: cannot convert endless range to an array
Также алиасирован как: 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;
}

Преобразует этот объект диапазона в печатную форму (используя to_s для преобразования объектов begin и end).

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

Spec-Zone.ru

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