[前][次][番号順一覧][スレッド一覧]

ruby-changes:52432

From: tarui <ko1@a...>
Date: Thu, 6 Sep 2018 04:06:15 +0900 (JST)
Subject: [ruby-changes:52432] tarui:r64640 (trunk): range.c: Range#cover? accepts Range object. [Feature #14473]

tarui	2018-09-06 04:06:08 +0900 (Thu, 06 Sep 2018)

  New Revision: 64640

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=64640

  Log:
    range.c: Range#cover? accepts Range object. [Feature #14473]
    
    * range.c (range_cover): add code for range argument.
              If the argument is a Range, check it is or is not 
      covered by the reciver. If it can be treated as a
      sequence, this method treats it that way. 
    * test/ruby/test_range.rb (class TestRange): add tests
      for this feature.
      
      This patch is written by Owen Stephens. thank you!

  Modified files:
    trunk/NEWS
    trunk/range.c
    trunk/test/ruby/test_range.rb
Index: NEWS
===================================================================
--- NEWS	(revision 64639)
+++ NEWS	(revision 64640)
@@ -176,6 +176,7 @@ sufficient information, see the ChangeLo https://github.com/ruby/ruby/blob/trunk/NEWS#L176
 
         * `Range#===` now uses `#cover?` instead of `#include?` method.
           [Feature #14575]
+	* `Range#cover?` now accepts Range object. [Feature #14473]
 
 * `RubyVM::AST`
 
Index: range.c
===================================================================
--- range.c	(revision 64639)
+++ range.c	(revision 64640)
@@ -1334,10 +1334,12 @@ range_include_internal(VALUE range, VALU https://github.com/ruby/ruby/blob/trunk/range.c#L1334
     return Qundef;
 }
 
+static int r_cover_range_p(VALUE range, VALUE beg, VALUE end, VALUE val);
 
 /*
  *  call-seq:
- *     rng.cover?(obj)  ->  true or false
+ *     rng.cover?(obj)   ->  true or false
+ *     rng.cover?(range) ->  true or false
  *
  *  Returns <code>true</code> if +obj+ is between the begin and end of
  *  the range.
@@ -1345,9 +1347,21 @@ range_include_internal(VALUE range, VALU https://github.com/ruby/ruby/blob/trunk/range.c#L1347
  *  This tests <code>begin <= obj <= end</code> when #exclude_end? is +false+
  *  and <code>begin <= obj < end</code> when #exclude_end? is +true+.
  *
- *     ("a".."z").cover?("c")    #=> true
- *     ("a".."z").cover?("5")    #=> false
- *     ("a".."z").cover?("cc")   #=> true
+ *  Returns <code>true</code> for a Range when it is covered by the reciver,
+ *  by comparing the begin and end values. If the argument can be treated as
+ *  a sequence, this method treats it that way. In the specific case of
+ *  <code>(a..b).cover?(c...d)</code> with <code>a <= c && b < d</code>,
+ *  end of sequence must be calculated, which may exhibit poor performance if
+ *  c is non-numeric. Returns <code>false</code> if the begin value of the
+ *  Range is larger than the end value.
+ *
+ *  Return
+ *     ("a".."z").cover?("c")  #=> true
+ *     ("a".."z").cover?("5")  #=> false
+ *     ("a".."z").cover?("cc") #=> true
+ *     (1..5).cover?(2..3)     #=> true
+ *     (1..5).cover?(0..6)     #=> false
+ *     (1..5).cover?(1...6)    #=> true
  */
 
 static VALUE
@@ -1357,10 +1371,49 @@ range_cover(VALUE range, VALUE val) https://github.com/ruby/ruby/blob/trunk/range.c#L1371
 
     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);
 }
 
 static VALUE
+r_call_max(VALUE r)
+{
+    return rb_funcallv(r, rb_intern("max"), 0, 0);
+}
+
+static int
+r_cover_range_p(VALUE range, VALUE beg, VALUE end, VALUE val)
+{
+    VALUE val_beg, val_end, val_max;
+    int cmp_end;
+
+    val_beg = RANGE_BEG(val);
+    val_end = RANGE_END(val);
+
+    if (!NIL_P(end) && NIL_P(val_end)) return FALSE;
+    if (!NIL_P(val_end) && r_less(val_beg, val_end) > -EXCL(val)) return FALSE;
+    if (!r_cover_p(range, beg, end, val_beg)) return FALSE;
+
+    cmp_end = r_less(end, val_end);
+
+    if (EXCL(range) == EXCL(val)) {
+        return cmp_end >= 0;
+    } else if (EXCL(range)) {
+        return cmp_end > 0;
+    } else if (cmp_end >= 0) {
+        return TRUE;
+    }
+
+    val_max = rb_rescue2(r_call_max, val, NULL, Qnil, rb_eTypeError, (VALUE)0);
+    if (val_max == Qnil) return FALSE;
+
+    return r_less(end, val_max) >= 0;
+}
+
+static VALUE
 r_cover_p(VALUE range, VALUE beg, VALUE end, VALUE val)
 {
     if (r_less(beg, val) <= 0) {
Index: test/ruby/test_range.rb
===================================================================
--- test/ruby/test_range.rb	(revision 64639)
+++ test/ruby/test_range.rb	(revision 64640)
@@ -525,6 +525,52 @@ class TestRange < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_range.rb#L525
     assert_not_operator(5..., :cover?, 0)
     assert_not_operator(5..., :cover?, "a")
     assert_operator(5.., :cover?, 10)
+
+    assert_operator(2..5, :cover?, 2..5)
+    assert_operator(2...6, :cover?, 2...6)
+    assert_operator(2...6, :cover?, 2..5)
+    assert_operator(2..5, :cover?, 2...6)
+    assert_operator(2..5, :cover?, 2..4)
+    assert_operator(2..5, :cover?, 2...4)
+    assert_operator(2..5, :cover?, 2...5)
+    assert_operator(2..5, :cover?, 3..5)
+    assert_operator(2..5, :cover?, 3..4)
+    assert_operator(2..5, :cover?, 3...6)
+    assert_operator(2...6, :cover?, 2...5)
+    assert_operator(2...6, :cover?, 2..5)
+    assert_operator(2..6, :cover?, 2...6)
+    assert_operator(2.., :cover?, 2..)
+    assert_operator(2.., :cover?, 3..)
+    assert_operator(1.., :cover?, 1..10)
+    assert_operator(2.0..5.0, :cover?, 2..3)
+    assert_operator(2..5, :cover?, 2.0..3.0)
+    assert_operator(2..5, :cover?, 2.0...3.0)
+    assert_operator(2..5, :cover?, 2.0...5.0)
+    assert_operator(2.0..5.0, :cover?, 2.0...3.0)
+    assert_operator(2.0..5.0, :cover?, 2.0...5.0)
+    assert_operator('aa'..'zz', :cover?, 'aa'...'bb')
+
+    assert_not_operator(2..5, :cover?, 1..5)
+    assert_not_operator(2...6, :cover?, 1..5)
+    assert_not_operator(2..5, :cover?, 1...6)
+    assert_not_operator(1..3, :cover?, 1...6)
+    assert_not_operator(2..5, :cover?, 2..6)
+    assert_not_operator(2...6, :cover?, 2..6)
+    assert_not_operator(2...6, :cover?, 2...7)
+    assert_not_operator(2..3, :cover?, 1..4)
+    assert_not_operator(1..2, :cover?, 1.0..3.0)
+    assert_not_operator(1.0..2.9, :cover?, 1.0..3.0)
+    assert_not_operator(1..2, :cover?, 4..3)
+    assert_not_operator(2..1, :cover?, 1..2)
+    assert_not_operator(1...2, :cover?, 1...3)
+    assert_not_operator(2.., :cover?, 1..)
+    assert_not_operator(2.., :cover?, 1..10)
+    assert_not_operator(1..10, :cover?, 1..)
+    assert_not_operator(1..5, :cover?, 3..2)
+    assert_not_operator(1..10, :cover?, 3...2)
+    assert_not_operator(1..10, :cover?, 3...3)
+    assert_not_operator('aa'..'zz', :cover?, 'aa'...'zzz')
+    assert_not_operator(1..10, :cover?, 1...10.1)
   end
 
   def test_beg_len

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]