класс Range
Объект Range представляет собой коллекцию значений, которые находятся между заданными начальным и конечным значениями.
Вы можете создать объект Range явно с помощью:
-
Литтерала диапазона:
# Ranges that use '..' to include the given end value. (1..4).to_a # => [1, 2, 3, 4] ('a'..'d').to_a # => ["a", "b", "c", "d"] # Ranges that use '...' to exclude the given end value. (1...4).to_a # => [1, 2, 3] ('a'...'d').to_a # => ["a", "b", "c"]
Диапазон может быть создан с помощью метода Range.new:
# 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 (но не другие классы, упомянутые выше).
Методы итератора включают:
-
Включенные из модуля 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.
Методы для преобразования
Публичные методы класса
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"]
Общедоступные методы экземпляров
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.
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?.
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"
# 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 и представлять этот объект.
static VALUE
range_begin(VALUE range)
{
return RANGE_BEG(range);
} Возвращает объект, определяющий начало self.
(1..4).begin # => 1 (..2).begin # => nil
Связанно с: Range#first, Range#end.
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, выбранный с помощью бинарного поиска.
См. Бинарный поиск.
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.
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?.
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).
Без блока возвращает перечислитель.
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.
Возвращает массив, содержащий элементы в 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.
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#==.
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
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
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.
Возвращает 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?.
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.
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
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.
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?.
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.
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 является диапазоном без начала или без конца.
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.
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#%.
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.
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.