範圍類別
範圍物件代表一個值集合,這些值介於給定的起點和終點值之間。
您可以使用以下方式明確建立範圍物件:
-
# 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
這裡的內容¶ ↑
首先,其他地方的內容。類別範圍
-
繼承自 類別 Object。
-
包含 模組 Enumerable,提供數十種其他方法。
在此,類別範圍提供對下列項目有用的方法
建立範圍的方法¶ ↑
-
::new
:傳回新的範圍。
查詢的方法¶ ↑
-
begin
:傳回給定self
的開始值。 -
bsearch
:傳回由二元搜尋從self
選取的元素。 -
count
:傳回self
中的元素計數。 -
end
:傳回給定self
的結束值。 -
exclude_end?
:傳回結束物件是否被排除。 -
first
:傳回self
的第一個元素。 -
hash
:傳回整數雜湊碼。 -
last
:傳回self
的最後一個元素。 -
max
:傳回self
中的最大值。 -
min
:傳回self
中的最小值。 -
minmax
:傳回self
中的最小值和最大值。 -
size
:傳回self
中的元素計數。
用於比較的函式¶ ↑
用於反覆運算的函式¶ ↑
-
%
:需要參數n
;呼叫區塊,並提供self
的每個第n
個元素。 -
each
:呼叫區塊,並提供self
的每個元素。 -
step
:接受可選參數n
(預設為 1);呼叫區塊,並提供self
的每個第n
個元素。
用於轉換的函式¶ ↑
用於處理 JSON 的函式¶ ↑
-
::json_create
:傳回由給定物件建構的新 Range 物件。 -
as_json
:傳回表示self
的 2 元素雜湊。 -
to_json
:傳回表示self
的 JSON 字串。
要讓這些方法可用
require 'json/add/range'
公開類別方法
請參閱 as_json
。
# File ext/json/lib/json/add/range.rb, line 9 def self.json_create(object) new(*object['a']) end
傳回一個根據給定物件 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_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; }
公開執行個體方法
重複運算 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"]
如果沒有給定區塊,則傳回一個列舉器,如果 self
是數字,則會是 Enumerator::ArithmeticSequence
類別;否則為 Enumerator 類別
e = (1..5) % 2 # => ((1..5).%(2)) e.class # => Enumerator::ArithmeticSequence ('a'..'e') % 2 # => #<Enumerator: ...>
相關:Range#step
。
static VALUE range_percent_step(VALUE range, VALUE step) { return range_step(1, &step, range); }
如果且僅如果下列情況成立,則傳回 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
(1..2) == (1..2.0) # => true (1..2).eql? (1..2.0) # => false
相關:Range#eql?
。
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); }
如果 object
介於 self.begin
和 self.end
之間,則傳回 true
。否則傳回 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 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"
static VALUE range_eqq(VALUE range, VALUE val) { return r_cover_p(range, RANGE_BEG(range), RANGE_END(range), val); }
方法 Range#as_json
和 Range.json_create
可用於序列化和取消序列化範圍物件;請參閱 Marshal
。
方法 Range#as_json
會序列化 self
,傳回表示 self
的 2 元素雜湊
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.json_create(x) # => 1..4 Range.json_create(y) # => 1...4 Range.json_create(z) # => "a".."d"
# 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
傳回定義 self
開頭的物件。
(1..4).begin # => 1 (..2).begin # => nil
相關:Range#first
、Range#end
。
static VALUE range_begin(VALUE range) { return RANGE_BEG(range); }
從 self
傳回一個由二元搜尋選取的元素。
請參閱 二元搜尋。
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; }
傳回元素的計數,根據提供的引數或區塊準則。
如果未提供引數且未給定區塊,則傳回元素的數目
(1..4).count # => 4 (1...4).count # => 3 ('a'..'d').count # => 4 ('a'...'d').count # => 3 (1..).count # => Infinity (..4).count # => Infinity
如果提供引數 object
,則傳回在 self
中找到的 object
數目,通常為 0 或 1
(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_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); }
如果提供的引數在 self
內,則傳回 true
,否則傳回 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_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); }
如果給定區塊,會將 self
的每個元素傳遞給區塊
a = [] (1..4).each {|element| a.push(element) } # => 1..4 a # => [1, 2, 3, 4]
除非 self.first.respond_to?(:succ)
,否則會引發例外狀況。
如果沒有給定區塊,會傳回一個列舉器。
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
結尾的物件。
(1..4).end # => 4 (1...4).end # => 4 (1..).end # => nil
static VALUE range_end(VALUE range) { return RANGE_END(range); }
如果且僅如果下列情況成立,則傳回 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
(1..2) == (1..2.0) # => true (1..2).eql? (1..2.0) # => false
相關:Range#==
。
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); }
如果 self
排除其結尾值,則傳回 true
;否則傳回 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_exclude_end_p(VALUE range) { return RBOOL(EXCL(range)); }
如果沒有參數,會傳回 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_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
的整數雜湊值。兩個範圍物件 r0
和 r1
僅在 r0.eql?(r1)
時才有相同的雜湊值。
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); }
如果 object
是 self
的元素,則傳回 true
;否則傳回 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
如果開頭和結尾是數字,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?
。
傳回 self
的字串表示,包括 begin.inspect
和 end.inspect
(1..4).inspect # => "1..4" (1...4).inspect # => "1...4" (1..).inspect # => "1.." (..4).inspect # => "..4"
('a'..'d').to_s # => "a..d" ('a'..'d').inspect # => "\"a\"..\"d\""
相關:Range#to_s
。
static VALUE range_inspect(VALUE range) { return rb_exec_recursive(inspect_range, range, 0); }
如果沒有引數,傳回 self
的最後一個元素(如果存在)
(1..4).last # => 4 ('a'..'d').last # => "d"
請注意,即使 exclude_end?
為 true
,沒有引數的 last
仍會傳回 self
的最後一個元素
(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]
請注意,如果 exclude_end?
為 true
,有引數的 last
就不會傳回 self
的最後一個元素
(1...4).last(3) # => [1, 2, 3] ('a'...'d').last(3) # => ["a", "b", "c"]
如果沒有最後一個元素,會引發例外狀況
(1..).last # Raises RangeError
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
中的最大值,使用 <=>
方法或給定的區塊進行比較。
如果沒有引數且沒有給定區塊,傳回 self
中最大值的元素。
(1..4).max # => 4 ('a'..'d').max # => "d" (-4..-1).max # => -1
如果給定非負整數引數 n
,且沒有給定區塊,傳回 self
中最大值的 n
個元素(在陣列中)
(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
是無開始範圍。
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).min # => 1 ('a'..'d').min # => "a" (-4..-1).min # => -4
如果提供非負整數引數 n
,但未提供區塊,則傳回 self
中最小值的 n
個元素,並儲存在陣列中
(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
是無限範圍。
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
中最小值和最大值的 2 個元素陣列,依比較方法 <=>
或指定的區塊而定。
如果未提供區塊,則傳回最小值和最大值,並使用 <=>
進行比較
(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
次數以比較元素;傳回包含 self
中最小值和最大值的 2 個元素 Array
,依區塊而定
(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_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) ); }
如果 range
與 self
重疊,則傳回 true
,否則傳回 false
(0..2).overlap?(1..3) #=> true (0..2).overlap?(3..4) #=> false (0..).overlap?(..0) #=> true
如果引數不是範圍,則會引發 TypeError
。
(1..3).overlap?(1) # TypeError
如果對 <=>
的內部呼叫傳回 nil
,則傳回 false
;亦即,運算元無法比較。
(1..3).overlap?('a'..'d') # => false
如果 self
或 range
為空,則傳回 false
。「空範圍」表示其開始值大於或等於(對於不包含範圍)其結束值。
(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
如果 `self` 和 `range` 中的起始值大於或等於(如果另一個是獨佔範圍)另一個的結束值,則傳回 `false`
(4..5).overlap?(2..3) # => false (4..5).overlap?(2...4) # => false (1..2).overlap?(3..4) # => false (1...3).overlap?(3..4) # => false
如果 `self` 和 `range` 中的結束值大於或等於(對於獨佔範圍)另一個的結束值,則傳回 `false`
(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_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 (!NIL_P(self_beg) && !NIL_P(other_beg)) { VALUE cmp = rb_funcall(self_beg, id_cmp, 1, other_beg); if (NIL_P(cmp)) return Qfalse; /* if both begin values are equal, no more comparisons needed */ if (rb_cmpint(cmp, self_beg, other_beg) == 0) return Qtrue; } else if (NIL_P(self_beg) && NIL_P(other_beg)) { VALUE cmp = rb_funcall(self_end, id_cmp, 1, other_end); return RBOOL(!NIL_P(cmp)); } 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; }
如果給定區塊,將 `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_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` 中的元素計數;否則,傳回 `nil`
(1..4).size # => 4 (1...4).size # => 3 (1..).size # => Infinity ('a'..'z').size #=> nil
相關:Range#count
。
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
的元素。
如果給定區塊且沒有參數,則呼叫區塊的範圍中每個元素;傳回 `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"]
如果沒有給定區塊,則傳回一個列舉器,如果 self
是數字,則會是 Enumerator::ArithmeticSequence
類別;否則為 Enumerator 類別
e = (1..5).step(2) # => ((1..5).step(2)) e.class # => Enumerator::ArithmeticSequence ('a'..'e').step # => #<Enumerator: ...>
相關:Range#%
。
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` 中元素的陣列(如果為有限集合);否則,引發例外狀況。
(1..4).to_a # => [1, 2, 3, 4] (1...4).to_a # => [1, 2, 3] ('a'..'d').to_a # => ["a", "b", "c", "d"]
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` 的 JSON
字串
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]}
# File ext/json/lib/json/add/range.rb, line 51 def to_json(*args) as_json.to_json(*args) end
傳回 `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"
('a'..'d').to_s # => "a..d" ('a'..'d').inspect # => "\"a\"..\"d\""
相關:Range#inspect
。
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; }