mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Introduce beginless range [Feature#14799]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@67422 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
dd2479bcea
commit
95f7992b89
8 changed files with 123 additions and 19 deletions
6
NEWS
6
NEWS
|
@ -28,6 +28,12 @@ sufficient information, see the ChangeLog file or Redmine
|
||||||
* Numbered parameter as the default block parameter is introduced as an
|
* Numbered parameter as the default block parameter is introduced as an
|
||||||
experimental feature. [Feature #4475]
|
experimental feature. [Feature #4475]
|
||||||
|
|
||||||
|
* A beginless range is experimentally introduced. It might not be as useful
|
||||||
|
as an endless range, but would be good for DSL purpose.
|
||||||
|
|
||||||
|
ary[..3] # identical to ary[0..3]
|
||||||
|
where(sales: ..100)
|
||||||
|
|
||||||
=== Core classes updates (outstanding ones only)
|
=== Core classes updates (outstanding ones only)
|
||||||
|
|
||||||
Enumerable::
|
Enumerable::
|
||||||
|
|
|
@ -76,6 +76,8 @@ firstline, predefined = __LINE__+1, %[\
|
||||||
token_ops = %[\
|
token_ops = %[\
|
||||||
Dot2 .. DOT2
|
Dot2 .. DOT2
|
||||||
Dot3 ... DOT3
|
Dot3 ... DOT3
|
||||||
|
BDot2 .. BDOT2
|
||||||
|
BDot3 ... BDOT3
|
||||||
UPlus +@ UPLUS
|
UPlus +@ UPLUS
|
||||||
UMinus -@ UMINUS
|
UMinus -@ UMINUS
|
||||||
Pow ** POW
|
Pow ** POW
|
||||||
|
|
|
@ -334,6 +334,7 @@ its ending value.
|
||||||
(1..2) # includes its ending value
|
(1..2) # includes its ending value
|
||||||
(1...2) # excludes its ending value
|
(1...2) # excludes its ending value
|
||||||
(1..) # endless range, representing infinite sequence from 1 to Infinity
|
(1..) # endless range, representing infinite sequence from 1 to Infinity
|
||||||
|
(..1) # beginless range, representing infinite sequence from -Infinity to 1
|
||||||
|
|
||||||
You may create a range of any object. See the Range documentation for details
|
You may create a range of any object. See the Range documentation for details
|
||||||
on the methods you need to implement.
|
on the methods you need to implement.
|
||||||
|
|
36
parse.y
36
parse.y
|
@ -922,6 +922,8 @@ static void token_info_warn(struct parser_params *p, const char *token, token_in
|
||||||
%token tNMATCH RUBY_TOKEN(NMATCH) "!~"
|
%token tNMATCH RUBY_TOKEN(NMATCH) "!~"
|
||||||
%token tDOT2 RUBY_TOKEN(DOT2) ".."
|
%token tDOT2 RUBY_TOKEN(DOT2) ".."
|
||||||
%token tDOT3 RUBY_TOKEN(DOT3) "..."
|
%token tDOT3 RUBY_TOKEN(DOT3) "..."
|
||||||
|
%token tBDOT2 RUBY_TOKEN(BDOT2) "(.."
|
||||||
|
%token tBDOT3 RUBY_TOKEN(BDOT3) "(..."
|
||||||
%token tAREF RUBY_TOKEN(AREF) "[]"
|
%token tAREF RUBY_TOKEN(AREF) "[]"
|
||||||
%token tASET RUBY_TOKEN(ASET) "[]="
|
%token tASET RUBY_TOKEN(ASET) "[]="
|
||||||
%token tLSHFT RUBY_TOKEN(LSHFT) "<<"
|
%token tLSHFT RUBY_TOKEN(LSHFT) "<<"
|
||||||
|
@ -968,7 +970,7 @@ static void token_info_warn(struct parser_params *p, const char *token, token_in
|
||||||
%right '=' tOP_ASGN
|
%right '=' tOP_ASGN
|
||||||
%left modifier_rescue
|
%left modifier_rescue
|
||||||
%right '?' ':'
|
%right '?' ':'
|
||||||
%nonassoc tDOT2 tDOT3
|
%nonassoc tDOT2 tDOT3 tBDOT2 tBDOT3
|
||||||
%left tOROP
|
%left tOROP
|
||||||
%left tANDOP
|
%left tANDOP
|
||||||
%nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH
|
%nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH
|
||||||
|
@ -1996,6 +1998,30 @@ arg : lhs '=' arg_rhs
|
||||||
/*% %*/
|
/*% %*/
|
||||||
/*% ripper: dot3!($1, Qnil) %*/
|
/*% ripper: dot3!($1, Qnil) %*/
|
||||||
}
|
}
|
||||||
|
| tBDOT2 arg
|
||||||
|
{
|
||||||
|
/*%%%*/
|
||||||
|
YYLTYPE loc;
|
||||||
|
loc.beg_pos = @1.beg_pos;
|
||||||
|
loc.end_pos = @1.beg_pos;
|
||||||
|
|
||||||
|
value_expr($2);
|
||||||
|
$$ = NEW_DOT2(new_nil(&loc), $2, &@$);
|
||||||
|
/*% %*/
|
||||||
|
/*% ripper: dot2!(Qnil, $2) %*/
|
||||||
|
}
|
||||||
|
| tBDOT3 arg
|
||||||
|
{
|
||||||
|
/*%%%*/
|
||||||
|
YYLTYPE loc;
|
||||||
|
loc.beg_pos = @1.beg_pos;
|
||||||
|
loc.end_pos = @1.beg_pos;
|
||||||
|
|
||||||
|
value_expr($2);
|
||||||
|
$$ = NEW_DOT3(new_nil(&loc), $2, &@$);
|
||||||
|
/*% %*/
|
||||||
|
/*% ripper: dot3!(Qnil, $2) %*/
|
||||||
|
}
|
||||||
| arg '+' arg
|
| arg '+' arg
|
||||||
{
|
{
|
||||||
$$ = call_bin_op(p, $1, '+', $3, &@2, &@$);
|
$$ = call_bin_op(p, $1, '+', $3, &@2, &@$);
|
||||||
|
@ -8241,15 +8267,16 @@ parser_yylex(struct parser_params *p)
|
||||||
pushback(p, c);
|
pushback(p, c);
|
||||||
return warn_balanced('-', "-", "unary operator");
|
return warn_balanced('-', "-", "unary operator");
|
||||||
|
|
||||||
case '.':
|
case '.': {
|
||||||
|
int is_beg = IS_BEG();
|
||||||
SET_LEX_STATE(EXPR_BEG);
|
SET_LEX_STATE(EXPR_BEG);
|
||||||
switch (c = nextc(p)) {
|
switch (c = nextc(p)) {
|
||||||
case '.':
|
case '.':
|
||||||
if ((c = nextc(p)) == '.') {
|
if ((c = nextc(p)) == '.') {
|
||||||
return tDOT3;
|
return is_beg ? tBDOT3 : tDOT3;
|
||||||
}
|
}
|
||||||
pushback(p, c);
|
pushback(p, c);
|
||||||
return tDOT2;
|
return is_beg ? tBDOT2 : tDOT2;
|
||||||
case ':':
|
case ':':
|
||||||
switch (c = nextc(p)) {
|
switch (c = nextc(p)) {
|
||||||
default:
|
default:
|
||||||
|
@ -8275,6 +8302,7 @@ parser_yylex(struct parser_params *p)
|
||||||
set_yylval_id('.');
|
set_yylval_id('.');
|
||||||
SET_LEX_STATE(EXPR_DOT);
|
SET_LEX_STATE(EXPR_DOT);
|
||||||
return '.';
|
return '.';
|
||||||
|
}
|
||||||
|
|
||||||
case '0': case '1': case '2': case '3': case '4':
|
case '0': case '1': case '2': case '3': case '4':
|
||||||
case '5': case '6': case '7': case '8': case '9':
|
case '5': case '6': case '7': case '8': case '9':
|
||||||
|
|
47
range.c
47
range.c
|
@ -35,7 +35,7 @@ static VALUE r_cover_p(VALUE, VALUE, VALUE, VALUE);
|
||||||
static void
|
static void
|
||||||
range_init(VALUE range, VALUE beg, VALUE end, VALUE exclude_end)
|
range_init(VALUE range, VALUE beg, VALUE end, VALUE exclude_end)
|
||||||
{
|
{
|
||||||
if ((!FIXNUM_P(beg) || !FIXNUM_P(end)) && !NIL_P(end)) {
|
if ((!FIXNUM_P(beg) || !FIXNUM_P(end)) && !NIL_P(beg) && !NIL_P(end)) {
|
||||||
VALUE v;
|
VALUE v;
|
||||||
|
|
||||||
v = rb_funcall(beg, id_cmp, 1, end);
|
v = rb_funcall(beg, id_cmp, 1, end);
|
||||||
|
@ -704,7 +704,7 @@ range_bsearch(VALUE range)
|
||||||
}
|
}
|
||||||
#if SIZEOF_DOUBLE == 8 && defined(HAVE_INT64_T)
|
#if SIZEOF_DOUBLE == 8 && defined(HAVE_INT64_T)
|
||||||
else if (RB_TYPE_P(beg, T_FLOAT) || RB_TYPE_P(end, T_FLOAT)) {
|
else if (RB_TYPE_P(beg, T_FLOAT) || RB_TYPE_P(end, T_FLOAT)) {
|
||||||
int64_t low = double_as_int64(RFLOAT_VALUE(rb_Float(beg)));
|
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 high = double_as_int64(NIL_P(end) ? HUGE_VAL : RFLOAT_VALUE(rb_Float(end)));
|
||||||
int64_t mid, org_high;
|
int64_t mid, org_high;
|
||||||
BSEARCH(int64_as_double_to_num);
|
BSEARCH(int64_as_double_to_num);
|
||||||
|
@ -726,6 +726,18 @@ range_bsearch(VALUE range)
|
||||||
diff = rb_funcall(diff, '*', 1, LONG2FIX(2));
|
diff = rb_funcall(diff, '*', 1, LONG2FIX(2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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) {
|
||||||
|
return bsearch_integer_range(mid, end, 0);
|
||||||
|
}
|
||||||
|
diff = rb_funcall(diff, '*', 1, LONG2FIX(2));
|
||||||
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
rb_raise(rb_eTypeError, "can't do binary search for %s", rb_obj_classname(beg));
|
rb_raise(rb_eTypeError, "can't do binary search for %s", rb_obj_classname(beg));
|
||||||
}
|
}
|
||||||
|
@ -770,6 +782,9 @@ range_size(VALUE range)
|
||||||
return DBL2NUM(HUGE_VAL);
|
return DBL2NUM(HUGE_VAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (NIL_P(b)) {
|
||||||
|
return DBL2NUM(HUGE_VAL);
|
||||||
|
}
|
||||||
|
|
||||||
return Qnil;
|
return Qnil;
|
||||||
}
|
}
|
||||||
|
@ -1230,7 +1245,7 @@ rb_range_beg_len(VALUE range, long *begp, long *lenp, long len, int err)
|
||||||
|
|
||||||
if (!rb_range_values(range, &b, &e, &excl))
|
if (!rb_range_values(range, &b, &e, &excl))
|
||||||
return Qfalse;
|
return Qfalse;
|
||||||
beg = NUM2LONG(b);
|
beg = NIL_P(b) ? 0 : NUM2LONG(b);
|
||||||
end = NIL_P(e) ? -1 : NUM2LONG(e);
|
end = NIL_P(e) ? -1 : NUM2LONG(e);
|
||||||
if (NIL_P(e)) excl = 0;
|
if (NIL_P(e)) excl = 0;
|
||||||
origbeg = beg;
|
origbeg = beg;
|
||||||
|
@ -1392,6 +1407,12 @@ range_include_internal(VALUE range, VALUE val)
|
||||||
VALUE rb_str_include_range_p(VALUE beg, VALUE end, VALUE val, VALUE exclusive);
|
VALUE rb_str_include_range_p(VALUE beg, VALUE end, VALUE val, VALUE exclusive);
|
||||||
return rb_str_include_range_p(beg, end, val, RANGE_EXCL(range));
|
return rb_str_include_range_p(beg, end, val, RANGE_EXCL(range));
|
||||||
}
|
}
|
||||||
|
else if (NIL_P(beg)) {
|
||||||
|
VALUE r = rb_funcall(val, id_cmp, 1, end);
|
||||||
|
if (NIL_P(r)) return Qfalse;
|
||||||
|
if (rb_cmpint(r, val, end) <= 0) return Qtrue;
|
||||||
|
return Qfalse;
|
||||||
|
}
|
||||||
else if (NIL_P(end)) {
|
else if (NIL_P(end)) {
|
||||||
VALUE r = rb_funcall(beg, id_cmp, 1, val);
|
VALUE r = rb_funcall(beg, id_cmp, 1, val);
|
||||||
if (NIL_P(r)) return Qfalse;
|
if (NIL_P(r)) return Qfalse;
|
||||||
|
@ -1487,7 +1508,7 @@ r_cover_range_p(VALUE range, VALUE beg, VALUE end, VALUE val)
|
||||||
static VALUE
|
static VALUE
|
||||||
r_cover_p(VALUE range, VALUE beg, VALUE end, VALUE val)
|
r_cover_p(VALUE range, VALUE beg, VALUE end, VALUE val)
|
||||||
{
|
{
|
||||||
if (r_less(beg, val) <= 0) {
|
if (NIL_P(beg) || r_less(beg, val) <= 0) {
|
||||||
int excl = EXCL(range);
|
int excl = EXCL(range);
|
||||||
if (NIL_P(end) || r_less(val, end) <= -excl)
|
if (NIL_P(end) || r_less(val, end) <= -excl)
|
||||||
return Qtrue;
|
return Qtrue;
|
||||||
|
@ -1550,9 +1571,15 @@ range_alloc(VALUE klass)
|
||||||
* ('a'..'e').to_a #=> ["a", "b", "c", "d", "e"]
|
* ('a'..'e').to_a #=> ["a", "b", "c", "d", "e"]
|
||||||
* ('a'...'e').to_a #=> ["a", "b", "c", "d"]
|
* ('a'...'e').to_a #=> ["a", "b", "c", "d"]
|
||||||
*
|
*
|
||||||
* == Endless Ranges
|
* == Beginless/Endless Ranges
|
||||||
|
*
|
||||||
|
* A "beginless range" and "endless range" represents a semi-infinite
|
||||||
|
* range. Literal notation for a beginless range is:
|
||||||
|
*
|
||||||
|
* (..1)
|
||||||
|
* # or
|
||||||
|
* (...1)
|
||||||
*
|
*
|
||||||
* An "endless range" represents a semi-infinite range.
|
|
||||||
* Literal notation for an endless range is:
|
* Literal notation for an endless range is:
|
||||||
*
|
*
|
||||||
* (1..)
|
* (1..)
|
||||||
|
@ -1564,14 +1591,16 @@ range_alloc(VALUE klass)
|
||||||
* (1..nil) # or similarly (1...nil)
|
* (1..nil) # or similarly (1...nil)
|
||||||
* Range.new(1, nil) # or Range.new(1, nil, true)
|
* Range.new(1, nil) # or Range.new(1, nil, true)
|
||||||
*
|
*
|
||||||
* Endless ranges are useful, for example, for idiomatic slicing of
|
* Beginless/endless ranges are useful, for example, for idiomatic
|
||||||
* arrays:
|
* slicing of arrays:
|
||||||
*
|
*
|
||||||
|
* [1, 2, 3, 4, 5][...2] # => [1, 2]
|
||||||
* [1, 2, 3, 4, 5][2...] # => [3, 4, 5]
|
* [1, 2, 3, 4, 5][2...] # => [3, 4, 5]
|
||||||
*
|
*
|
||||||
* Some implementation details:
|
* Some implementation details:
|
||||||
*
|
*
|
||||||
* * +end+ of endless range is +nil+;
|
* * +begin+ of beginless range and +end+ of endless range are +nil+;
|
||||||
|
* * +each+ of beginless range raises an exception;
|
||||||
* * +each+ of endless range enumerates infinite sequence (may be
|
* * +each+ of endless range enumerates infinite sequence (may be
|
||||||
* useful in combination with Enumerable#take_while or similar
|
* useful in combination with Enumerable#take_while or similar
|
||||||
* methods);
|
* methods);
|
||||||
|
|
|
@ -43,10 +43,26 @@ describe "Range.new" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "endless range" do
|
describe "beginless/endless range" do
|
||||||
|
ruby_version_is ""..."2.7" do
|
||||||
it "does not allow range without left boundary" do
|
it "does not allow range without left boundary" do
|
||||||
-> { Range.new(nil, 1) }.should raise_error(ArgumentError, /bad value for range/)
|
-> { Range.new(nil, 1) }.should raise_error(ArgumentError, /bad value for range/)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ruby_version_is "2.7" do
|
||||||
|
it "allows beginless left boundary" do
|
||||||
|
range = Range.new(nil, 1)
|
||||||
|
range.begin.should == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "distinguishes ranges with included and excluded right boundary" do
|
||||||
|
range_exclude = Range.new(nil, 1, true)
|
||||||
|
range_include = Range.new(nil, 1, false)
|
||||||
|
|
||||||
|
range_exclude.should_not == range_include
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
ruby_version_is ""..."2.6" do
|
ruby_version_is ""..."2.6" do
|
||||||
it "does not allow range without right boundary" do
|
it "does not allow range without right boundary" do
|
||||||
|
|
|
@ -42,6 +42,8 @@ class TestArray < Test::Unit::TestCase
|
||||||
assert_equal([1, 2, 3], x[1..3])
|
assert_equal([1, 2, 3], x[1..3])
|
||||||
assert_equal([1, 2, 3], x[1,3])
|
assert_equal([1, 2, 3], x[1,3])
|
||||||
assert_equal([3, 4, 5], x[3..])
|
assert_equal([3, 4, 5], x[3..])
|
||||||
|
assert_equal([0, 1, 2], x[..2])
|
||||||
|
assert_equal([0, 1], x[...2])
|
||||||
|
|
||||||
x[0, 2] = 10
|
x[0, 2] = 10
|
||||||
assert_equal([10, 2, 3, 4, 5], x)
|
assert_equal([10, 2, 3, 4, 5], x)
|
||||||
|
@ -375,8 +377,11 @@ class TestArray < Test::Unit::TestCase
|
||||||
|
|
||||||
assert_equal(@cls[10, 11, 12], a[9..11])
|
assert_equal(@cls[10, 11, 12], a[9..11])
|
||||||
assert_equal(@cls[98, 99, 100], a[97..])
|
assert_equal(@cls[98, 99, 100], a[97..])
|
||||||
|
assert_equal(@cls[1, 2, 3], a[..2])
|
||||||
|
assert_equal(@cls[1, 2], a[...2])
|
||||||
assert_equal(@cls[10, 11, 12], a[-91..-89])
|
assert_equal(@cls[10, 11, 12], a[-91..-89])
|
||||||
assert_equal(@cls[98, 99, 100], a[-3..])
|
assert_equal(@cls[1, 2, 3], a[..-98])
|
||||||
|
assert_equal(@cls[1, 2], a[...-98])
|
||||||
|
|
||||||
assert_nil(a[10, -3])
|
assert_nil(a[10, -3])
|
||||||
assert_equal [], a[10..7]
|
assert_equal [], a[10..7]
|
||||||
|
@ -462,6 +467,14 @@ class TestArray < Test::Unit::TestCase
|
||||||
assert_equal(nil, a[10..] = nil)
|
assert_equal(nil, a[10..] = nil)
|
||||||
assert_equal(@cls[*(0..9).to_a] + @cls[nil], a)
|
assert_equal(@cls[*(0..9).to_a] + @cls[nil], a)
|
||||||
|
|
||||||
|
a = @cls[*(0..99).to_a]
|
||||||
|
assert_equal(nil, a[..10] = nil)
|
||||||
|
assert_equal(@cls[nil] + @cls[*(11..99).to_a], a)
|
||||||
|
|
||||||
|
a = @cls[*(0..99).to_a]
|
||||||
|
assert_equal(nil, a[...10] = nil)
|
||||||
|
assert_equal(@cls[nil] + @cls[*(10..99).to_a], a)
|
||||||
|
|
||||||
a = @cls[1, 2, 3]
|
a = @cls[1, 2, 3]
|
||||||
a[1, 0] = a
|
a[1, 0] = a
|
||||||
assert_equal([1, 1, 2, 3, 2, 3], a)
|
assert_equal([1, 1, 2, 3, 2, 3], a)
|
||||||
|
|
|
@ -691,6 +691,8 @@ class TestRange < Test::Unit::TestCase
|
||||||
|
|
||||||
assert_equal Float::INFINITY, (1...).size
|
assert_equal Float::INFINITY, (1...).size
|
||||||
assert_equal Float::INFINITY, (1.0...).size
|
assert_equal Float::INFINITY, (1.0...).size
|
||||||
|
assert_equal Float::INFINITY, (...1).size
|
||||||
|
assert_equal Float::INFINITY, (...1.0).size
|
||||||
assert_nil ("a"...).size
|
assert_nil ("a"...).size
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -735,6 +737,7 @@ class TestRange < Test::Unit::TestCase
|
||||||
assert_equal(1, (0...ary.size).bsearch {|i| ary[i] >= 100 })
|
assert_equal(1, (0...ary.size).bsearch {|i| ary[i] >= 100 })
|
||||||
|
|
||||||
assert_equal(1_000_001, (0...).bsearch {|i| i > 1_000_000 })
|
assert_equal(1_000_001, (0...).bsearch {|i| i > 1_000_000 })
|
||||||
|
assert_equal( -999_999, (...0).bsearch {|i| i > -1_000_000 })
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_bsearch_for_float
|
def test_bsearch_for_float
|
||||||
|
@ -788,6 +791,7 @@ class TestRange < Test::Unit::TestCase
|
||||||
assert_in_delta(7.0, (0.0..10).bsearch {|x| 7.0 - x })
|
assert_in_delta(7.0, (0.0..10).bsearch {|x| 7.0 - x })
|
||||||
|
|
||||||
assert_equal( 1_000_000.0.next_float, (0.0..).bsearch {|x| x > 1_000_000 })
|
assert_equal( 1_000_000.0.next_float, (0.0..).bsearch {|x| x > 1_000_000 })
|
||||||
|
assert_equal(-1_000_000.0.next_float, (..0.0).bsearch {|x| x > -1_000_000 })
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_bsearch_values(range, search, a)
|
def check_bsearch_values(range, search, a)
|
||||||
|
@ -890,6 +894,7 @@ class TestRange < Test::Unit::TestCase
|
||||||
assert_equal(bignum + 0, (bignum...bignum+ary.size).bsearch {|i| true })
|
assert_equal(bignum + 0, (bignum...bignum+ary.size).bsearch {|i| true })
|
||||||
assert_equal(nil, (bignum...bignum+ary.size).bsearch {|i| false })
|
assert_equal(nil, (bignum...bignum+ary.size).bsearch {|i| false })
|
||||||
assert_equal(bignum * 2 + 1, (bignum...).bsearch {|i| i > bignum * 2 })
|
assert_equal(bignum * 2 + 1, (bignum...).bsearch {|i| i > bignum * 2 })
|
||||||
|
assert_equal(-bignum * 2 + 1, (...-bignum).bsearch {|i| i > -bignum * 2 })
|
||||||
|
|
||||||
assert_raise(TypeError) { ("a".."z").bsearch {} }
|
assert_raise(TypeError) { ("a".."z").bsearch {} }
|
||||||
end
|
end
|
||||||
|
@ -907,4 +912,8 @@ class TestRange < Test::Unit::TestCase
|
||||||
assert_equal([1,2,3,4], (1...5).to_a)
|
assert_equal([1,2,3,4], (1...5).to_a)
|
||||||
assert_raise(RangeError) { (1..).to_a }
|
assert_raise(RangeError) { (1..).to_a }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_beginless_range_iteration
|
||||||
|
assert_raise(TypeError) { (..1).each { } }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue