класс Range
Объект 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 (но не другие классы, упомянутые выше).
Методы итератора включают:
-
Включенные из модуля 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.
-
Методы для сравнения
-
-
=== -
Возвращает, находится ли данный объект между начальным и конечным значениями.
-
-
-
cover? -
Возвращает, находится ли данный объект внутри
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_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"
# 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
Если 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?.
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 {
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.
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?.
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.
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 является диапазоном без начала или без конца.
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.
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.