Skip to content

Commit 9ca7389

Browse files
committed
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! git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64640 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
1 parent 19dec2a commit 9ca7389

3 files changed

Lines changed: 104 additions & 4 deletions

File tree

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ sufficient information, see the ChangeLog file or Redmine
176176
177177
* `Range#===` now uses `#cover?` instead of `#include?` method.
178178
[Feature #14575]
179+
* `Range#cover?` now accepts Range object. [Feature #14473]
179180
180181
* `RubyVM::AST`
181182

range.c

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1334,20 +1334,34 @@ range_include_internal(VALUE range, VALUE val)
13341334
return Qundef;
13351335
}
13361336

1337+
static int r_cover_range_p(VALUE range, VALUE beg, VALUE end, VALUE val);
13371338

13381339
/*
13391340
* call-seq:
1340-
* rng.cover?(obj) -> true or false
1341+
* rng.cover?(obj) -> true or false
1342+
* rng.cover?(range) -> true or false
13411343
*
13421344
* Returns <code>true</code> if +obj+ is between the begin and end of
13431345
* the range.
13441346
*
13451347
* This tests <code>begin <= obj <= end</code> when #exclude_end? is +false+
13461348
* and <code>begin <= obj < end</code> when #exclude_end? is +true+.
13471349
*
1348-
* ("a".."z").cover?("c") #=> true
1349-
* ("a".."z").cover?("5") #=> false
1350-
* ("a".."z").cover?("cc") #=> true
1350+
* Returns <code>true</code> for a Range when it is covered by the reciver,
1351+
* by comparing the begin and end values. If the argument can be treated as
1352+
* a sequence, this method treats it that way. In the specific case of
1353+
* <code>(a..b).cover?(c...d)</code> with <code>a <= c && b < d</code>,
1354+
* end of sequence must be calculated, which may exhibit poor performance if
1355+
* c is non-numeric. Returns <code>false</code> if the begin value of the
1356+
* Range is larger than the end value.
1357+
*
1358+
* Return
1359+
* ("a".."z").cover?("c") #=> true
1360+
* ("a".."z").cover?("5") #=> false
1361+
* ("a".."z").cover?("cc") #=> true
1362+
* (1..5).cover?(2..3) #=> true
1363+
* (1..5).cover?(0..6) #=> false
1364+
* (1..5).cover?(1...6) #=> true
13511365
*/
13521366

13531367
static VALUE
@@ -1357,9 +1371,48 @@ range_cover(VALUE range, VALUE val)
13571371

13581372
beg = RANGE_BEG(range);
13591373
end = RANGE_END(range);
1374+
1375+
if (rb_obj_is_kind_of(val, rb_cRange)) {
1376+
return RBOOL(r_cover_range_p(range, beg, end, val));
1377+
}
13601378
return r_cover_p(range, beg, end, val);
13611379
}
13621380

1381+
static VALUE
1382+
r_call_max(VALUE r)
1383+
{
1384+
return rb_funcallv(r, rb_intern("max"), 0, 0);
1385+
}
1386+
1387+
static int
1388+
r_cover_range_p(VALUE range, VALUE beg, VALUE end, VALUE val)
1389+
{
1390+
VALUE val_beg, val_end, val_max;
1391+
int cmp_end;
1392+
1393+
val_beg = RANGE_BEG(val);
1394+
val_end = RANGE_END(val);
1395+
1396+
if (!NIL_P(end) && NIL_P(val_end)) return FALSE;
1397+
if (!NIL_P(val_end) && r_less(val_beg, val_end) > -EXCL(val)) return FALSE;
1398+
if (!r_cover_p(range, beg, end, val_beg)) return FALSE;
1399+
1400+
cmp_end = r_less(end, val_end);
1401+
1402+
if (EXCL(range) == EXCL(val)) {
1403+
return cmp_end >= 0;
1404+
} else if (EXCL(range)) {
1405+
return cmp_end > 0;
1406+
} else if (cmp_end >= 0) {
1407+
return TRUE;
1408+
}
1409+
1410+
val_max = rb_rescue2(r_call_max, val, NULL, Qnil, rb_eTypeError, (VALUE)0);
1411+
if (val_max == Qnil) return FALSE;
1412+
1413+
return r_less(end, val_max) >= 0;
1414+
}
1415+
13631416
static VALUE
13641417
r_cover_p(VALUE range, VALUE beg, VALUE end, VALUE val)
13651418
{

test/ruby/test_range.rb

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,52 @@ def test_cover
525525
assert_not_operator(5..., :cover?, 0)
526526
assert_not_operator(5..., :cover?, "a")
527527
assert_operator(5.., :cover?, 10)
528+
529+
assert_operator(2..5, :cover?, 2..5)
530+
assert_operator(2...6, :cover?, 2...6)
531+
assert_operator(2...6, :cover?, 2..5)
532+
assert_operator(2..5, :cover?, 2...6)
533+
assert_operator(2..5, :cover?, 2..4)
534+
assert_operator(2..5, :cover?, 2...4)
535+
assert_operator(2..5, :cover?, 2...5)
536+
assert_operator(2..5, :cover?, 3..5)
537+
assert_operator(2..5, :cover?, 3..4)
538+
assert_operator(2..5, :cover?, 3...6)
539+
assert_operator(2...6, :cover?, 2...5)
540+
assert_operator(2...6, :cover?, 2..5)
541+
assert_operator(2..6, :cover?, 2...6)
542+
assert_operator(2.., :cover?, 2..)
543+
assert_operator(2.., :cover?, 3..)
544+
assert_operator(1.., :cover?, 1..10)
545+
assert_operator(2.0..5.0, :cover?, 2..3)
546+
assert_operator(2..5, :cover?, 2.0..3.0)
547+
assert_operator(2..5, :cover?, 2.0...3.0)
548+
assert_operator(2..5, :cover?, 2.0...5.0)
549+
assert_operator(2.0..5.0, :cover?, 2.0...3.0)
550+
assert_operator(2.0..5.0, :cover?, 2.0...5.0)
551+
assert_operator('aa'..'zz', :cover?, 'aa'...'bb')
552+
553+
assert_not_operator(2..5, :cover?, 1..5)
554+
assert_not_operator(2...6, :cover?, 1..5)
555+
assert_not_operator(2..5, :cover?, 1...6)
556+
assert_not_operator(1..3, :cover?, 1...6)
557+
assert_not_operator(2..5, :cover?, 2..6)
558+
assert_not_operator(2...6, :cover?, 2..6)
559+
assert_not_operator(2...6, :cover?, 2...7)
560+
assert_not_operator(2..3, :cover?, 1..4)
561+
assert_not_operator(1..2, :cover?, 1.0..3.0)
562+
assert_not_operator(1.0..2.9, :cover?, 1.0..3.0)
563+
assert_not_operator(1..2, :cover?, 4..3)
564+
assert_not_operator(2..1, :cover?, 1..2)
565+
assert_not_operator(1...2, :cover?, 1...3)
566+
assert_not_operator(2.., :cover?, 1..)
567+
assert_not_operator(2.., :cover?, 1..10)
568+
assert_not_operator(1..10, :cover?, 1..)
569+
assert_not_operator(1..5, :cover?, 3..2)
570+
assert_not_operator(1..10, :cover?, 3...2)
571+
assert_not_operator(1..10, :cover?, 3...3)
572+
assert_not_operator('aa'..'zz', :cover?, 'aa'...'zzz')
573+
assert_not_operator(1..10, :cover?, 1...10.1)
528574
end
529575

530576
def test_beg_len

0 commit comments

Comments
 (0)