класс 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:
-
Наследуется от класса Объект.
-
Включает модуль Enumerable, который предоставляет десятки дополнительных методов.
Здесь класс Range предоставляет методы, полезные для:
Методы создания диапазона
-
::new: Возвращает новый диапазон.
Методы запросов
-
begin: Возвращает начальное значение, заданное дляself. -
bsearch: Возвращает элемент изself, выбранный с помощью двоичного поиска. -
count: Возвращает количество элементов вself. -
end: Возвращает конечное значение, заданное дляself. -
exclude_end?: Возвращает значение, исключает ли конечный объект. -
first: Возвращает первые элементыself. -
hash: Возвращает целочисленный хеш-код. -
last: Возвращает последние элементыself. -
max: Возвращает максимальные значения вself. -
min: Возвращает минимальные значения вself. -
minmax: Возвращает минимальное и максимальное значения вself. -
size: Возвращает количество элементов вself.
Методы сравнения
-
==: Возвращает, равны ли данный объект иself(использует==). -
===: Возвращает, находится ли данный объект между начальным и конечным значениями. -
cover?: Возвращает, содержится ли данный объект вself. -
eql?: Возвращает, равны ли данный объект иself(используетeql?). -
include?(алиасmember?): Возвращает, является ли данный объект элементомself.
Методы итерации
-
%: Требует аргументn; вызывает блок с каждымn-м элементомself. -
each: Вызывает блок с каждым элементомself. -
step: Принимает необязательный аргументn(по умолчанию 1); вызывает блок с каждымn-м элементомself.
Методы преобразования
-
inspect: Возвращает строковое представлениеself(используетinspect). -
to_s: Возвращает строковое представлениеself(используетto_s).
Методы работы с JSON
-
::json_create: Возвращает новый объект Range, построенный из данного объекта. -
as_json: Возвращает 2-элементный хэш, представляющийself. -
to_json: Возвращает JSON-строку, представляющуюself.
Для того, чтобы эти методы были доступны:
require 'json/add/range'
Методы публичного класса
# File ext/json/lib/json/add/range.rb, line 9 def self.json_create(object) new(*object['a']) end
См. as_json.
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)
{
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 31
def as_json(*)
{
JSON.create_id => self.class.name,
'a' => [ first, last, exclude_end? ]
}
end Методы Range#as_json и Range.json_create могут использоваться для сериализации и десериализации объекта Range; см. Marshal.
Метод Range#as_json сериализует self, возвращая хеш из 2 элементов, представляющий self:
require 'json/add/range'
x = (1..4).as_json # => {"json_class"=>"Range", "a"=>[1, 4, false]}
y = (1...4).as_json # => {"json_class"=>"Range", "a"=>[1, 4, true]}
z = ('a'..'d').as_json # => {"json_class"=>"Range", "a"=>["a", "d", false]}
Метод JSON.create десериализует такой хеш, возвращая объект Range:
Range.json_create(x) # => 1..4 Range.json_create(y) # => 1...4 Range.json_create(z) # => "a".."d"
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).
*
* The half-open interval (low, high] indicates where the target is located.
* The loop continues until low and high are adjacent.
*
* -1/2 can be either 0 or -1 in C89. However, when low and high are not adjacent,
* the rounding direction of mid = (low + high) / 2 does not affect the result of
* the binary search.
*
* 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, excl) \
do { \
RETURN_ENUMERATOR(range, 0, 0); \
if (!(excl)) high++; \
low--; \
while (low + 1 < high) { \
mid = ((high < 0) == (low < 0)) ? low + ((high - low) / 2) \
: (low + high) / 2; \
BSEARCH_CHECK(conv(mid)); \
if (smaller) { \
high = mid; \
} \
else { \
low = mid; \
} \
} \
return satisfied; \
} while (0)
#define BSEARCH_FIXNUM(beg, end, excl) \
do { \
long low = FIX2LONG(beg); \
long high = FIX2LONG(end); \
long mid; \
BSEARCH(INT2FIX, (excl)); \
} while (0)
beg = RANGE_BEG(range);
end = RANGE_END(range);
if (FIXNUM_P(beg) && FIXNUM_P(end)) {
BSEARCH_FIXNUM(beg, end, EXCL(range));
}
#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;
BSEARCH(int64_as_double_to_num, EXCL(range));
}
#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) {
if (FIXNUM_P(beg) && FIXNUM_P(mid)) {
BSEARCH_FIXNUM(beg, mid, false);
}
else {
return bsearch_integer_range(beg, mid, false);
}
}
diff = rb_funcall(diff, '*', 1, LONG2FIX(2));
beg = mid;
}
}
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) {
if (FIXNUM_P(mid) && FIXNUM_P(end)) {
BSEARCH_FIXNUM(mid, end, false);
}
else {
return bsearch_integer_range(mid, end, false);
}
}
diff = rb_funcall(diff, '*', 1, LONG2FIX(2));
end = mid;
}
}
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);
}
VALUE beg = RANGE_BEG(range), end = RANGE_END(range);
if (NIL_P(beg) || NIL_P(end)) {
/* We are confident that the answer is Infinity. */
return DBL2NUM(HUGE_VAL);
}
if (is_integer_p(beg)) {
VALUE size = range_size(range);
if (!NIL_P(size)) {
return size;
}
}
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; то есть операнды несравнимы.
Диапазоны без начала охватывают все значения того же типа до конца, исключая конец для исключающих диапазонов. Диапазоны без начала охватывают диапазоны, заканчивающиеся до конца диапазона без начала, или в конце диапазона без начала для включительных диапазонов.
(..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.
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?.
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_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)
);
} Возвращает массив из 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_overlap(VALUE range, VALUE other)
{
if (!rb_obj_is_kind_of(other, rb_cRange)) {
rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Range)",
rb_class_name(rb_obj_class(other)));
}
VALUE self_beg = RANGE_BEG(range);
VALUE self_end = RANGE_END(range);
int self_excl = EXCL(range);
VALUE other_beg = RANGE_BEG(other);
VALUE other_end = RANGE_END(other);
int other_excl = EXCL(other);
if (empty_region_p(self_beg, other_end, other_excl)) return Qfalse;
if (empty_region_p(other_beg, self_end, self_excl)) return Qfalse;
/* if both begin values are equal, no more comparisons needed */
if (rb_equal(self_beg, other_beg)) return Qtrue;
if (empty_region_p(self_beg, self_end, self_excl)) return Qfalse;
if (empty_region_p(other_beg, other_end, other_excl)) return Qfalse;
return Qtrue;
} Возвращает true если range перекрывается с self, иначе false.
(0..2).overlap?(1..3) #=> true (0..2).overlap?(3..4) #=> false (0..).overlap?(..0) #=> true
При недиапазонном аргументе, выбрасывает TypeError.
(1..3).overlap?(1) # TypeError
Возвращает false если внутренний вызов <=> возвращает nil; то есть операнды не сравнимы.
(1..3).overlap?('a'..'d') # => false
Возвращает false если self или range пустой. «Пустой диапазон» означает, что его начальное значение больше или равно для исключающего диапазона, его конечному значению.
(4..1).overlap?(2..3) # => false (4..1).overlap?(..3) # => false (4..1).overlap?(2..) # => false (2...2).overlap?(1..2) # => false (1..4).overlap?(3..2) # => false (..4).overlap?(3..2) # => false (1..).overlap?(3..2) # => false (1..2).overlap?(2...2) # => false
Возвращает false если начальное значение одного из self и range больше или равно, если другое — исключающий диапазон, конечному значению другого.
(4..5).overlap?(2..3) # => false (4..5).overlap?(2...4) # => false (1..2).overlap?(3..4) # => false (1...3).overlap?(3..4) # => false
Возвращает false если конечное значение одного из self и range больше или равно, если другое — исключающий диапазон, конечному значению другого.
(4..5).overlap?(2..3) # => false (4..5).overlap?(2...4) # => false (1..2).overlap?(3..4) # => false (1...3).overlap?(3..4) # => false
Обратите внимание, что метод не делает предположений о том, что диапазон без начала фактически пустой, даже если его верхняя граница является минимально возможным значением его типа, поэтому всё это вернёт true.
(...-Float::INFINITY).overlap?(...-Float::INFINITY) # => true (..."").overlap?(..."") # => true (...[]).overlap?(...[]) # => true
Даже если эти диапазоны фактически пустые (ни одно число не может быть меньше -Float::INFINITY), они всё равно считаются перекрывающимися с собой.
Связанные: Range#cover?.
static VALUE
range_reverse_each(VALUE range)
{
RETURN_SIZED_ENUMERATOR(range, 0, 0, range_enum_size);
VALUE beg = RANGE_BEG(range);
VALUE end = RANGE_END(range);
int excl = EXCL(range);
if (NIL_P(end)) {
rb_raise(rb_eTypeError, "can't iterate from %s",
rb_obj_classname(end));
}
if (FIXNUM_P(beg) && FIXNUM_P(end)) {
if (excl) {
if (end == LONG2FIX(FIXNUM_MIN)) return range;
end = rb_int_minus(end, INT2FIX(1));
}
range_reverse_each_fixnum_section(beg, end);
}
else if ((NIL_P(beg) || RB_INTEGER_TYPE_P(beg)) && RB_INTEGER_TYPE_P(end)) {
if (excl) {
end = rb_int_minus(end, INT2FIX(1));
}
range_reverse_each_positive_bignum_section(beg, end);
range_reverse_each_fixnum_section(beg, end);
range_reverse_each_negative_bignum_section(beg, end);
}
else {
return rb_call_super(0, NULL);
}
return range;
} При наличии блока, передаёт каждый элемент self в блок в обратном порядке:
a = []
(1..4).reverse_each {|element| a.push(element) } # => 1..4
a # => [4, 3, 2, 1]
a = []
(1...4).reverse_each {|element| a.push(element) } # => 1...4
a # => [3, 2, 1]
Без блока возвращает итератор.
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));
}
if (!NIL_P(e))
range_each_func(range, step_i, (VALUE)iter);
else
for (;; b = rb_funcallv(b, id_succ, 0, 0))
step_i(b, (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"]
# File ext/json/lib/json/add/range.rb, line 51 def to_json(*args) as_json.to_json(*args) end
Возвращает строку JSON, представляющую self:
require 'json/add/range'
puts (1..4).to_json
puts (1...4).to_json
puts ('a'..'d').to_json
Вывод:
{"json_class":"Range","a":[1,4,false]}
{"json_class":"Range","a":[1,4,true]}
{"json_class":"Range","a":["a","d",false]}
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.