From bec4d053400c3a11c8efd68c3e8bd5ea4a0bcc54 Mon Sep 17 00:00:00 2001 From: taiyoslime Date: Mon, 5 Oct 2020 19:53:05 +0900 Subject: Introduce endless range (a part of #5085) Co-Authored-By: n4o847 <22975590+n4o847@users.noreply.github.com> Co-Authored-By: smallkirby --- mrbgems/mruby-range-ext/mrblib/range.rb | 16 +++++++++++----- mrbgems/mruby-range-ext/src/range.c | 16 ++++++++++++++++ mrbgems/mruby-range-ext/test/range.rb | 29 ++++++++++++++++++++++++++--- 3 files changed, 53 insertions(+), 8 deletions(-) (limited to 'mrbgems/mruby-range-ext') diff --git a/mrbgems/mruby-range-ext/mrblib/range.rb b/mrbgems/mruby-range-ext/mrblib/range.rb index a213beb57..8b0969003 100644 --- a/mrbgems/mruby-range-ext/mrblib/range.rb +++ b/mrbgems/mruby-range-ext/mrblib/range.rb @@ -27,10 +27,12 @@ class Range end def max(&block) - val = self.first - last = self.last + val = self.begin + last = self.end return super if block + raise RangeError, "cannot get the maximum of endless range" if last.nil? + # fast path for numerics if val.kind_of?(Numeric) && last.kind_of?(Numeric) raise TypeError if exclude_end? && !last.kind_of?(Fixnum) @@ -47,9 +49,13 @@ class Range end def min(&block) - val = self.first - last = self.last - return super if block + val = self.begin + last = self.end + if block + raise RangeError, "cannot get the minimum of endless range with custom comparison method" if last.nil? + return super + end + return val if last.nil? # fast path for numerics if val.kind_of?(Numeric) && last.kind_of?(Numeric) diff --git a/mrbgems/mruby-range-ext/src/range.c b/mrbgems/mruby-range-ext/src/range.c index 2a0b4e97d..c263b484b 100644 --- a/mrbgems/mruby-range-ext/src/range.c +++ b/mrbgems/mruby-range-ext/src/range.c @@ -42,6 +42,9 @@ range_cover(mrb_state *mrb, mrb_value range) end = RANGE_END(r); if (r_le(mrb, beg, val)) { + if (mrb_nil_p(end)) { + return mrb_true_value(); + } if (RANGE_EXCL(r)) { if (r_lt(mrb, val, end)) return mrb_true_value(); @@ -76,6 +79,11 @@ range_last(mrb_state *mrb, mrb_value range) { mrb_value num; mrb_value array; + struct RRange *r = mrb_range_ptr(mrb, range); + + if (mrb_nil_p(RANGE_END(r))) { + mrb_raise(mrb, E_RANGE_ERROR, "cannot get the last element of endless range"); + } if (mrb_get_args(mrb, "|o", &num) == 0) { return mrb_range_end(mrb, range); @@ -108,6 +116,10 @@ range_size(mrb_state *mrb, mrb_value range) beg = RANGE_BEG(r); end = RANGE_END(r); + if ((mrb_fixnum_p(beg) || mrb_float_p(beg)) && mrb_nil_p(end)) { + return mrb_float_value(mrb, INFINITY); + } + excl = RANGE_EXCL(r); if (mrb_fixnum_p(beg)) { beg_f = (mrb_float)mrb_fixnum(beg); @@ -159,6 +171,10 @@ range_size(mrb_state *mrb, mrb_value range) beg = RANGE_BEG(r); end = RANGE_END(r); + if (mrb_fixnum_p(beg) && mrb_nil_p(end)) { + return mrb_nil_value(); + } + excl = RANGE_EXCL(r) ? 0 : 1; if (mrb_fixnum_p(beg) && mrb_fixnum_p(end)) { diff --git a/mrbgems/mruby-range-ext/test/range.rb b/mrbgems/mruby-range-ext/test/range.rb index 865e46d02..169ba7169 100644 --- a/mrbgems/mruby-range-ext/test/range.rb +++ b/mrbgems/mruby-range-ext/test/range.rb @@ -5,11 +5,16 @@ assert('Range#cover?') do assert_true ("a".."z").cover?("c") assert_true !("a".."z").cover?("5") assert_true ("a".."z").cover?("cc") + assert_true ("a"..).cover?("c") + assert_false ("a"..).cover?("5") + assert_true ("a"..).cover?("cc") end assert('Range#first') do assert_equal 10, (10..20).first assert_equal [10, 11, 12], (10..20).first(3) + assert_equal 10, (10..).first + assert_equal [10, 11, 12], (10..).first(3) skip unless Object.const_defined?(:Float) assert_equal [0, 1, 2], (0..Float::INFINITY).first(3) @@ -18,6 +23,8 @@ end assert('Range#last') do assert_equal 20, (10..20).last assert_equal 20, (10...20).last + assert_raise(RangeError) { (10..).last } + assert_raise(RangeError) { (10...).last } assert_equal [18, 19, 20], (10..20).last(3) assert_equal [17, 18, 19], (10...20).last(3) end @@ -26,6 +33,9 @@ assert('Range#size') do assert_equal 42, (1..42).size assert_equal 41, (1...42).size assert_nil ('a'..'z').size + assert_nil ('a'..).size + + assert_nil (1..).size unless Object.const_defined?(:Float) skip unless Object.const_defined?(:Float) assert_equal 6, (1...6.3).size @@ -33,6 +43,10 @@ assert('Range#size') do assert_equal 5, (1.1...6).size assert_equal 15, (1.0..15.9).size assert_equal Float::INFINITY, (0..Float::INFINITY).size + + assert_equal Float::INFINITY, (1..).size + assert_equal Float::INFINITY, (1...).size + assert_equal Float::INFINITY, (1.0..).size end assert('Range#max') do @@ -50,6 +64,10 @@ assert('Range#max') do # returns the endpoint when the endpoint equals the start point and the range is inclusive assert_equal 5, (5..5).max + # raises RangeError when called on an endless range + assert_raise(RangeError) { (10..).max } + assert_raise(RangeError) { (10...).max } + skip unless Object.const_defined?(:Float) # returns the maximum value in the Float range when called with no arguments @@ -94,26 +112,31 @@ assert('Range#min') do # returns the minimum value in the range when called with no arguments assert_equal 1, (1..10).min assert_equal 1, (1...10).min + assert_equal 1, (1..).min # returns nil when the start point is greater than the endpoint assert_equal nil, (100..10).min # returns nil when the endpoint equals the start point and the range is exclusive - assert_equal nil, (5...5).max + assert_equal nil, (5...5).min # returns the endpoint when the endpoint equals the start point and the range is inclusive - assert_equal 5, (5..5).max + assert_equal 5, (5..5).min skip unless Object.const_defined?(:Float) # returns the minimum value in the Float range when called with no arguments assert_equal 303.20, (303.20..908.1111).min + assert_equal 1, (1.0..).min # returns nil when the start point is greater than the endpoint in a Float range - assert_equal nil, (3003.20..908.1111).max + assert_equal nil, (3003.20..908.1111).min end assert('Range#min given a block') do + # raise when called with a block in endless range + assert_raise(RangeError) { (1..).min{} } + # passes each pair of values in the range to the block acc = [] (1..10).min do |a, b| -- cgit v1.2.3