1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Introduce endless range [Feature#12912]

Typical usages:
```
p ary[1..]          # drop the first element; identical to ary[1..-1]
(1..).each {|n|...} # iterate forever from 1; identical to 1.step{...}
```

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@63192 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
mame 2018-04-19 15:18:50 +00:00
parent 3a5d1e4b77
commit 7f95eed19e
7 changed files with 208 additions and 29 deletions

View file

@ -1404,7 +1404,7 @@ enum ruby_num_rounding_mode {
int rb_num_to_uint(VALUE val, unsigned int *ret); int rb_num_to_uint(VALUE val, unsigned int *ret);
VALUE ruby_num_interval_step_size(VALUE from, VALUE to, VALUE step, int excl); VALUE ruby_num_interval_step_size(VALUE from, VALUE to, VALUE step, int excl);
int ruby_float_step(VALUE from, VALUE to, VALUE step, int excl); int ruby_float_step(VALUE from, VALUE to, VALUE step, int excl, int allow_endless);
double ruby_float_mod(double x, double y); double ruby_float_mod(double x, double y);
int rb_num_negative_p(VALUE); int rb_num_negative_p(VALUE);
VALUE rb_int_succ(VALUE num); VALUE rb_int_succ(VALUE num);
@ -2013,6 +2013,7 @@ VALUE rb_gcd_gmp(VALUE x, VALUE y);
/* internal use */ /* internal use */
VALUE rb_setup_fake_str(struct RString *fake_str, const char *name, long len, rb_encoding *enc); VALUE rb_setup_fake_str(struct RString *fake_str, const char *name, long len, rb_encoding *enc);
#endif #endif
VALUE rb_str_upto_endless_each(VALUE, VALUE (*each)(), VALUE);
/* thread.c (export) */ /* thread.c (export) */
int ruby_thread_has_gvl_p(void); /* for ext/fiddle/closure.c */ int ruby_thread_has_gvl_p(void); /* for ext/fiddle/closure.c */

View file

@ -2469,11 +2469,11 @@ ruby_float_step_size(double beg, double end, double unit, int excl)
} }
int int
ruby_float_step(VALUE from, VALUE to, VALUE step, int excl) ruby_float_step(VALUE from, VALUE to, VALUE step, int excl, int allow_endless)
{ {
if (RB_TYPE_P(from, T_FLOAT) || RB_TYPE_P(to, T_FLOAT) || RB_TYPE_P(step, T_FLOAT)) { if (RB_TYPE_P(from, T_FLOAT) || RB_TYPE_P(to, T_FLOAT) || RB_TYPE_P(step, T_FLOAT)) {
double beg = NUM2DBL(from); double beg = NUM2DBL(from);
double end = NUM2DBL(to); double end = (allow_endless && NIL_P(to)) ? HUGE_VAL : NUM2DBL(to);
double unit = NUM2DBL(step); double unit = NUM2DBL(step);
double n = ruby_float_step_size(beg, end, unit, excl); double n = ruby_float_step_size(beg, end, unit, excl);
long i; long i;
@ -2712,7 +2712,7 @@ num_step(int argc, VALUE *argv, VALUE from)
} }
} }
} }
else if (!ruby_float_step(from, to, step, FALSE)) { else if (!ruby_float_step(from, to, step, FALSE, FALSE)) {
VALUE i = from; VALUE i = from;
if (inf) { if (inf) {

16
parse.y
View file

@ -1903,6 +1903,22 @@ arg : lhs '=' arg_rhs
/*% %*/ /*% %*/
/*% ripper: dot3!($1, $3) %*/ /*% ripper: dot3!($1, $3) %*/
} }
| arg tDOT2
{
/*%%%*/
value_expr($1);
$$ = NEW_DOT2($1, new_nil(&@$), &@$);
/*% %*/
/*% ripper: dot2!($1, Qnil) %*/
}
| arg tDOT3
{
/*%%%*/
value_expr($1);
$$ = NEW_DOT3($1, new_nil(&@$), &@$);
/*% %*/
/*% ripper: dot3!($1, Qnil) %*/
}
| arg '+' arg | arg '+' arg
{ {
$$ = call_bin_op(p, $1, '+', $3, &@2, &@$); $$ = call_bin_op(p, $1, '+', $3, &@2, &@$);

74
range.c
View file

@ -37,7 +37,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)) { if ((!FIXNUM_P(beg) || !FIXNUM_P(end)) && !NIL_P(end)) {
VALUE v; VALUE v;
v = rb_funcall(beg, id_cmp, 1, end); v = rb_funcall(beg, id_cmp, 1, end);
@ -400,7 +400,19 @@ range_step(int argc, VALUE *argv, VALUE range)
step = check_step_domain(step); step = check_step_domain(step);
} }
if (FIXNUM_P(b) && FIXNUM_P(e) && FIXNUM_P(step)) { /* fixnums are special */ if (FIXNUM_P(b) && NIL_P(e) && FIXNUM_P(step)) {
long i = FIX2LONG(b), unit = FIX2LONG(step);
while (1) {
rb_yield(LONG2FIX(i));
if (i + unit < i) break;
i += unit;
}
b = LONG2NUM(i);
for (;; b = rb_funcallv(b, id_succ, 0, 0))
rb_yield(b);
}
else if (FIXNUM_P(b) && FIXNUM_P(e) && FIXNUM_P(step)) { /* fixnums are special */
long end = FIX2LONG(e); long end = FIX2LONG(e);
long i, unit = FIX2LONG(step); long i, unit = FIX2LONG(step);
@ -414,16 +426,21 @@ range_step(int argc, VALUE *argv, VALUE range)
} }
} }
else if (SYMBOL_P(b) && SYMBOL_P(e)) { /* symbols are special */ else if (SYMBOL_P(b) && (NIL_P(e) || SYMBOL_P(e))) { /* symbols are special */
VALUE args[2], iter[2]; VALUE args[2], iter[2];
args[0] = rb_sym2str(e);
args[1] = EXCL(range) ? Qtrue : Qfalse;
iter[0] = INT2FIX(1); iter[0] = INT2FIX(1);
iter[1] = step; iter[1] = step;
if (NIL_P(e)) {
rb_str_upto_endless_each(rb_sym2str(b), sym_step_i, (VALUE)iter);
}
else {
args[0] = rb_sym2str(e);
args[1] = EXCL(range) ? Qtrue : Qfalse;
rb_block_call(rb_sym2str(b), rb_intern("upto"), 2, args, sym_step_i, (VALUE)iter); rb_block_call(rb_sym2str(b), rb_intern("upto"), 2, args, sym_step_i, (VALUE)iter);
} }
else if (ruby_float_step(b, e, step, EXCL(range))) { }
else if (ruby_float_step(b, e, step, EXCL(range), TRUE)) {
/* done */ /* done */
} }
else if (rb_obj_is_kind_of(b, rb_cNumeric) || else if (rb_obj_is_kind_of(b, rb_cNumeric) ||
@ -433,7 +450,7 @@ range_step(int argc, VALUE *argv, VALUE range)
VALUE v = b; VALUE v = b;
int i = 0; int i = 0;
while (RTEST(rb_funcall(v, op, 1, e))) { while (NIL_P(e) || RTEST(rb_funcall(v, op, 1, e))) {
rb_yield(v); rb_yield(v);
i++; i++;
v = rb_funcall(b, '+', 1, rb_funcall(INT2NUM(i), '*', 1, step)); v = rb_funcall(b, '+', 1, rb_funcall(INT2NUM(i), '*', 1, step));
@ -446,12 +463,18 @@ range_step(int argc, VALUE *argv, VALUE range)
VALUE args[2], iter[2]; VALUE args[2], iter[2];
b = tmp; b = tmp;
args[0] = e;
args[1] = EXCL(range) ? Qtrue : Qfalse;
iter[0] = INT2FIX(1); iter[0] = INT2FIX(1);
iter[1] = step; iter[1] = step;
if (NIL_P(e)) {
rb_str_upto_endless_each(b, step_i, (VALUE)iter);
}
else {
args[0] = e;
args[1] = EXCL(range) ? Qtrue : Qfalse;
rb_block_call(b, rb_intern("upto"), 2, args, step_i, (VALUE)iter); rb_block_call(b, rb_intern("upto"), 2, args, step_i, (VALUE)iter);
} }
}
else { else {
VALUE args[2]; VALUE args[2];
@ -746,7 +769,18 @@ range_each(VALUE range)
beg = RANGE_BEG(range); beg = RANGE_BEG(range);
end = RANGE_END(range); end = RANGE_END(range);
if (FIXNUM_P(beg) && FIXNUM_P(end)) { /* fixnums are special */ if (FIXNUM_P(beg) && NIL_P(end)) {
long i = FIX2LONG(beg);
while (FIXABLE(i)) {
rb_yield(LONG2FIX(i++));
}
beg = LONG2NUM(i);
inf_loop:
for (;; beg = rb_funcallv(beg, id_succ, 0, 0))
rb_yield(beg);
}
else if (FIXNUM_P(beg) && FIXNUM_P(end)) { /* fixnums are special */
long lim = FIX2LONG(end); long lim = FIX2LONG(end);
long i; long i;
@ -767,18 +801,27 @@ range_each(VALUE range)
VALUE tmp = rb_check_string_type(beg); VALUE tmp = rb_check_string_type(beg);
if (!NIL_P(tmp)) { if (!NIL_P(tmp)) {
if (!NIL_P(end)) {
VALUE args[2]; VALUE args[2];
args[0] = end; args[0] = end;
args[1] = EXCL(range) ? Qtrue : Qfalse; args[1] = EXCL(range) ? Qtrue : Qfalse;
rb_block_call(tmp, rb_intern("upto"), 2, args, each_i, 0); rb_block_call(tmp, rb_intern("upto"), 2, args, each_i, 0);
} }
else if (RB_TYPE_P(beg, T_STRING)) {
rb_str_upto_endless_each(beg, each_i, 0);
}
else goto inf_loop;
}
else { else {
if (!discrete_object_p(beg)) { if (!discrete_object_p(beg)) {
rb_raise(rb_eTypeError, "can't iterate from %s", rb_raise(rb_eTypeError, "can't iterate from %s",
rb_obj_classname(beg)); rb_obj_classname(beg));
} }
if (!NIL_P(end))
range_each_func(range, each_i, 0); range_each_func(range, each_i, 0);
else
goto inf_loop;
} }
} }
return range; return range;
@ -1012,7 +1055,8 @@ 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 = NUM2LONG(b);
end = NUM2LONG(e); end = NIL_P(e) ? -1 : NUM2LONG(e);
if (NIL_P(e)) excl = 0;
origbeg = beg; origbeg = beg;
origend = end; origend = end;
if (beg < 0) { if (beg < 0) {
@ -1072,16 +1116,16 @@ range_to_s(VALUE range)
static VALUE static VALUE
inspect_range(VALUE range, VALUE dummy, int recur) inspect_range(VALUE range, VALUE dummy, int recur)
{ {
VALUE str, str2; VALUE str, str2 = Qundef;
if (recur) { if (recur) {
return rb_str_new2(EXCL(range) ? "(... ... ...)" : "(... .. ...)"); return rb_str_new2(EXCL(range) ? "(... ... ...)" : "(... .. ...)");
} }
str = rb_inspect(RANGE_BEG(range)); str = rb_inspect(RANGE_BEG(range));
str2 = rb_inspect(RANGE_END(range)); if (!NIL_P(RANGE_END(range))) str2 = rb_inspect(RANGE_END(range));
str = rb_str_dup(str); str = rb_str_dup(str);
rb_str_cat(str, "...", EXCL(range) ? 3 : 2); rb_str_cat(str, "...", EXCL(range) ? 3 : 2);
rb_str_append(str, str2); if (str2 != Qundef) rb_str_append(str, str2);
OBJ_INFECT(str, range); OBJ_INFECT(str, range);
return str; return str;

View file

@ -4325,6 +4325,50 @@ str_upto_each(VALUE beg, VALUE end, int excl, int (*each)(VALUE, VALUE), VALUE a
return beg; return beg;
} }
VALUE
rb_str_upto_endless_each(VALUE beg, VALUE (*each)(VALUE, VALUE), VALUE arg)
{
VALUE current;
ID succ;
CONST_ID(succ, "succ");
/* both edges are all digits */
if (is_ascii_string(beg) && ISDIGIT(RSTRING_PTR(beg)[0]) &&
all_digits_p(RSTRING_PTR(beg), RSTRING_LEN(beg))) {
VALUE b, args[2], fmt = rb_fstring_cstr("%.*d");
int width = RSTRING_LENINT(beg);
b = rb_str_to_inum(beg, 10, FALSE);
if (FIXNUM_P(b)) {
long bi = FIX2LONG(b);
rb_encoding *usascii = rb_usascii_encoding();
while (FIXABLE(bi)) {
(*each)(rb_enc_sprintf(usascii, "%.*ld", width, bi), arg);
bi++;
}
b = LONG2NUM(bi);
}
args[0] = INT2FIX(width);
while (1) {
args[1] = b;
(*each)(rb_str_format(numberof(args), args, fmt), arg);
b = rb_funcallv(b, succ, 0, 0);
}
}
/* normal case */
current = rb_str_dup(beg);
while (1) {
VALUE next = rb_funcallv(current, succ, 0, 0);
(*each)(current, arg);
current = next;
StringValue(current);
if (RSTRING_LEN(current) == 0)
break;
}
return beg;
}
static int static int
include_range_i(VALUE str, VALUE arg) include_range_i(VALUE str, VALUE arg)
{ {

View file

@ -41,6 +41,7 @@ class TestArray < Test::Unit::TestCase
assert_equal(2, x[2]) assert_equal(2, x[2])
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..])
x[0, 2] = 10 x[0, 2] = 10
assert_equal([10, 2, 3, 4, 5], x) assert_equal([10, 2, 3, 4, 5], x)
@ -199,6 +200,8 @@ class TestArray < Test::Unit::TestCase
assert_equal([0, 1, 2, 13, 4, 5], [0, 1, 2, 3, 4, 5].fill(3...4){|i| i+10}) assert_equal([0, 1, 2, 13, 4, 5], [0, 1, 2, 3, 4, 5].fill(3...4){|i| i+10})
assert_equal([0, 1, 12, 13, 14, 5], [0, 1, 2, 3, 4, 5].fill(2..-2){|i| i+10}) assert_equal([0, 1, 12, 13, 14, 5], [0, 1, 2, 3, 4, 5].fill(2..-2){|i| i+10})
assert_equal([0, 1, 12, 13, 4, 5], [0, 1, 2, 3, 4, 5].fill(2...-2){|i| i+10}) assert_equal([0, 1, 12, 13, 4, 5], [0, 1, 2, 3, 4, 5].fill(2...-2){|i| i+10})
assert_equal([0, 1, 2, 13, 14, 15], [0, 1, 2, 3, 4, 5].fill(3..){|i| i+10})
assert_equal([0, 1, 2, 13, 14, 15], [0, 1, 2, 3, 4, 5].fill(3...){|i| i+10})
end end
# From rubicon # From rubicon
@ -346,7 +349,9 @@ class TestArray < Test::Unit::TestCase
assert_equal(@cls[99], a[-2..-2]) assert_equal(@cls[99], a[-2..-2])
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[10, 11, 12], a[-91..-89]) assert_equal(@cls[10, 11, 12], a[-91..-89])
assert_equal(@cls[98, 99, 100], a[-3..])
assert_nil(a[10, -3]) assert_nil(a[10, -3])
assert_equal [], a[10..7] assert_equal [], a[10..7]
@ -428,6 +433,10 @@ class TestArray < Test::Unit::TestCase
assert_equal(nil, a[10..19] = nil) assert_equal(nil, a[10..19] = nil)
assert_equal(@cls[*(0..9).to_a] + @cls[nil] + @cls[*(20..99).to_a], a) assert_equal(@cls[*(0..9).to_a] + @cls[nil] + @cls[*(20..99).to_a], a)
a = @cls[*(0..99).to_a]
assert_equal(nil, a[10..] = nil)
assert_equal(@cls[*(0..9).to_a] + @cls[nil], 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)
@ -1378,9 +1387,12 @@ class TestArray < Test::Unit::TestCase
assert_equal(@cls[99], a.slice(-2..-2)) assert_equal(@cls[99], a.slice(-2..-2))
assert_equal(@cls[10, 11, 12], a.slice(9..11)) assert_equal(@cls[10, 11, 12], a.slice(9..11))
assert_equal(@cls[98, 99, 100], a.slice(97..))
assert_equal(@cls[10, 11, 12], a.slice(-91..-89))
assert_equal(@cls[10, 11, 12], a.slice(-91..-89)) assert_equal(@cls[10, 11, 12], a.slice(-91..-89))
assert_nil(a.slice(-101..-1)) assert_nil(a.slice(-101..-1))
assert_nil(a.slice(-101..))
assert_nil(a.slice(10, -3)) assert_nil(a.slice(10, -3))
assert_equal @cls[], a.slice(10..7) assert_equal @cls[], a.slice(10..7)
@ -2106,6 +2118,7 @@ class TestArray < Test::Unit::TestCase
assert_raise(IndexError) { [0][LONGP] = 2 } assert_raise(IndexError) { [0][LONGP] = 2 }
assert_raise(IndexError) { [0][(LONGP + 1) / 2 - 1] = 2 } assert_raise(IndexError) { [0][(LONGP + 1) / 2 - 1] = 2 }
assert_raise(IndexError) { [0][LONGP..-1] = 2 } assert_raise(IndexError) { [0][LONGP..-1] = 2 }
assert_raise(IndexError) { [0][LONGP..] = 2 }
a = [0] a = [0]
a[2] = 4 a[2] = 4
assert_equal([0, nil, 4], a) assert_equal([0, nil, 4], a)

View file

@ -12,6 +12,9 @@ class TestRange < Test::Unit::TestCase
assert_raise(ArgumentError) { (1.."3") } assert_raise(ArgumentError) { (1.."3") }
assert_equal((0..nil), Range.new(0, nil, false))
assert_equal((0...nil), Range.new(0, nil, true))
obj = Object.new obj = Object.new
def obj.<=>(other) def obj.<=>(other)
raise RuntimeError, "cmp" raise RuntimeError, "cmp"
@ -31,14 +34,17 @@ class TestRange < Test::Unit::TestCase
assert_equal(["a"], ("a" .. "a").to_a) assert_equal(["a"], ("a" .. "a").to_a)
assert_equal(["a"], ("a" ... "b").to_a) assert_equal(["a"], ("a" ... "b").to_a)
assert_equal(["a", "b"], ("a" .. "b").to_a) assert_equal(["a", "b"], ("a" .. "b").to_a)
assert_equal([*"a".."z", "aa"], ("a"..).take(27))
end end
def test_range_numeric_string def test_range_numeric_string
assert_equal(["6", "7", "8"], ("6".."8").to_a, "[ruby-talk:343187]") assert_equal(["6", "7", "8"], ("6".."8").to_a, "[ruby-talk:343187]")
assert_equal(["6", "7"], ("6"..."8").to_a) assert_equal(["6", "7"], ("6"..."8").to_a)
assert_equal(["9", "10"], ("9".."10").to_a) assert_equal(["9", "10"], ("9".."10").to_a)
assert_equal(["9", "10"], ("9"..).take(2))
assert_equal(["09", "10"], ("09".."10").to_a, "[ruby-dev:39361]") assert_equal(["09", "10"], ("09".."10").to_a, "[ruby-dev:39361]")
assert_equal(["9", "10"], (SimpleDelegator.new("9").."10").to_a) assert_equal(["9", "10"], (SimpleDelegator.new("9").."10").to_a)
assert_equal(["9", "10"], (SimpleDelegator.new("9")..).take(2))
assert_equal(["9", "10"], ("9"..SimpleDelegator.new("10")).to_a) assert_equal(["9", "10"], ("9"..SimpleDelegator.new("10")).to_a)
end end
@ -123,9 +129,10 @@ class TestRange < Test::Unit::TestCase
assert_equal(r, Marshal.load(Marshal.dump(r))) assert_equal(r, Marshal.load(Marshal.dump(r)))
r = 1...2 r = 1...2
assert_equal(r, Marshal.load(Marshal.dump(r))) assert_equal(r, Marshal.load(Marshal.dump(r)))
s = Marshal.dump(r) r = (1..)
s.sub!(/endi./n, 'end0') assert_equal(r, Marshal.load(Marshal.dump(r)))
assert_raise(ArgumentError) {Marshal.load(s)} r = (1...)
assert_equal(r, Marshal.load(Marshal.dump(r)))
end end
def test_bad_value def test_bad_value
@ -135,6 +142,8 @@ class TestRange < Test::Unit::TestCase
def test_exclude_end def test_exclude_end
assert_not_predicate(0..1, :exclude_end?) assert_not_predicate(0..1, :exclude_end?)
assert_predicate(0...1, :exclude_end?) assert_predicate(0...1, :exclude_end?)
assert_not_predicate(0.., :exclude_end?)
assert_predicate(0..., :exclude_end?)
end end
def test_eq def test_eq
@ -145,8 +154,17 @@ class TestRange < Test::Unit::TestCase
assert_not_equal(r, (1..2)) assert_not_equal(r, (1..2))
assert_not_equal(r, (0..2)) assert_not_equal(r, (0..2))
assert_not_equal(r, (0...1)) assert_not_equal(r, (0...1))
assert_not_equal(r, (0..nil))
subclass = Class.new(Range) subclass = Class.new(Range)
assert_equal(r, subclass.new(0,1)) assert_equal(r, subclass.new(0,1))
r = (0..nil)
assert_equal(r, r)
assert_equal(r, (0..nil))
assert_not_equal(r, 0)
assert_not_equal(r, (0...nil))
subclass = Class.new(Range)
assert_equal(r, subclass.new(0,nil))
end end
def test_eql def test_eql
@ -159,12 +177,22 @@ class TestRange < Test::Unit::TestCase
assert_not_operator(r, :eql?, 0...1) assert_not_operator(r, :eql?, 0...1)
subclass = Class.new(Range) subclass = Class.new(Range)
assert_operator(r, :eql?, subclass.new(0,1)) assert_operator(r, :eql?, subclass.new(0,1))
r = (0..nil)
assert_operator(r, :eql?, r)
assert_operator(r, :eql?, 0..nil)
assert_not_operator(r, :eql?, 0)
assert_not_operator(r, :eql?, 0...nil)
subclass = Class.new(Range)
assert_operator(r, :eql?, subclass.new(0,nil))
end end
def test_hash def test_hash
assert_kind_of(Integer, (0..1).hash) assert_kind_of(Integer, (0..1).hash)
assert_equal((0..1).hash, (0..1).hash) assert_equal((0..1).hash, (0..1).hash)
assert_not_equal((0..1).hash, (0...1).hash) assert_not_equal((0..1).hash, (0...1).hash)
assert_equal((0..nil).hash, (0..nil).hash)
assert_not_equal((0..nil).hash, (0...nil).hash)
end end
def test_step def test_step
@ -172,17 +200,31 @@ class TestRange < Test::Unit::TestCase
(0..10).step {|x| a << x } (0..10).step {|x| a << x }
assert_equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], a) assert_equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], a)
a = []
(0..).step {|x| a << x; break if a.size == 10 }
assert_equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], a)
a = [] a = []
(0..10).step(2) {|x| a << x } (0..10).step(2) {|x| a << x }
assert_equal([0, 2, 4, 6, 8, 10], a) assert_equal([0, 2, 4, 6, 8, 10], a)
a = []
(0..).step(2) {|x| a << x; break if a.size == 10 }
assert_equal([0, 2, 4, 6, 8, 10, 12, 14, 16, 18], a)
assert_raise(ArgumentError) { (0..10).step(-1) { } } assert_raise(ArgumentError) { (0..10).step(-1) { } }
assert_raise(ArgumentError) { (0..10).step(0) { } } assert_raise(ArgumentError) { (0..10).step(0) { } }
assert_raise(ArgumentError) { (0..).step(-1) { } }
assert_raise(ArgumentError) { (0..).step(0) { } }
a = [] a = []
("a" .. "z").step(2) {|x| a << x } ("a" .. "z").step(2) {|x| a << x }
assert_equal(%w(a c e g i k m o q s u w y), a) assert_equal(%w(a c e g i k m o q s u w y), a)
a = []
("a" .. ).step(2) {|x| a << x; break if a.size == 13 }
assert_equal(%w(a c e g i k m o q s u w y), a)
a = [] a = []
("a" .. "z").step(2**32) {|x| a << x } ("a" .. "z").step(2**32) {|x| a << x }
assert_equal(["a"], a) assert_equal(["a"], a)
@ -192,12 +234,16 @@ class TestRange < Test::Unit::TestCase
assert_equal([4294967295, 4294967297], a) assert_equal([4294967295, 4294967297], a)
zero = (2**32).coerce(0).first zero = (2**32).coerce(0).first
assert_raise(ArgumentError) { (2**32-1 .. 2**32+1).step(zero) { } } assert_raise(ArgumentError) { (2**32-1 .. 2**32+1).step(zero) { } }
a = []
(2**32-1 .. ).step(2) {|x| a << x; break if a.size == 2 }
assert_equal([4294967295, 4294967297], a)
o1 = Object.new o1 = Object.new
o2 = Object.new o2 = Object.new
def o1.<=>(x); -1; end def o1.<=>(x); -1; end
def o2.<=>(x); 0; end def o2.<=>(x); 0; end
assert_raise(TypeError) { (o1..o2).step(1) { } } assert_raise(TypeError) { (o1..o2).step(1) { } }
assert_raise(TypeError) { (o1..).step(1) { } }
class << o1; self; end.class_eval do class << o1; self; end.class_eval do
define_method(:succ) { o2 } define_method(:succ) { o2 }
@ -216,6 +262,10 @@ class TestRange < Test::Unit::TestCase
(0..2).step(0.5) {|x| a << x } (0..2).step(0.5) {|x| a << x }
assert_equal([0, 0.5, 1.0, 1.5, 2.0], a) assert_equal([0, 0.5, 1.0, 1.5, 2.0], a)
a = []
(0..).step(0.5) {|x| a << x; break if a.size == 5 }
assert_equal([0, 0.5, 1.0, 1.5, 2.0], a)
a = [] a = []
(0x40000000..0x40000002).step(0.5) {|x| a << x } (0x40000000..0x40000002).step(0.5) {|x| a << x }
assert_equal([1073741824, 1073741824.5, 1073741825.0, 1073741825.5, 1073741826], a) assert_equal([1073741824, 1073741824.5, 1073741825.0, 1073741825.5, 1073741826], a)
@ -239,6 +289,10 @@ class TestRange < Test::Unit::TestCase
(0..10).each {|x| a << x } (0..10).each {|x| a << x }
assert_equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], a) assert_equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], a)
a = []
(0..).each {|x| a << x; break if a.size == 10 }
assert_equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], a)
o1 = Object.new o1 = Object.new
o2 = Object.new o2 = Object.new
def o1.setcmp(v) @cmpresult = v end def o1.setcmp(v) @cmpresult = v end
@ -285,6 +339,9 @@ class TestRange < Test::Unit::TestCase
assert_equal(0, (0..1).begin) assert_equal(0, (0..1).begin)
assert_equal(1, (0..1).end) assert_equal(1, (0..1).end)
assert_equal(1, (0...1).end) assert_equal(1, (0...1).end)
assert_equal(0, (0..nil).begin)
assert_equal(nil, (0..nil).end)
assert_equal(nil, (0...nil).end)
end end
def test_first_last def test_first_last
@ -308,6 +365,8 @@ class TestRange < Test::Unit::TestCase
def test_to_s def test_to_s
assert_equal("0..1", (0..1).to_s) assert_equal("0..1", (0..1).to_s)
assert_equal("0...1", (0...1).to_s) assert_equal("0...1", (0...1).to_s)
assert_equal("0..", (0..nil).to_s)
assert_equal("0...", (0...nil).to_s)
bug11767 = '[ruby-core:71811] [Bug #11767]' bug11767 = '[ruby-core:71811] [Bug #11767]'
assert_predicate(("0".taint.."1").to_s, :tainted?, bug11767) assert_predicate(("0".taint.."1").to_s, :tainted?, bug11767)
@ -318,6 +377,8 @@ class TestRange < Test::Unit::TestCase
def test_inspect def test_inspect
assert_equal("0..1", (0..1).inspect) assert_equal("0..1", (0..1).inspect)
assert_equal("0...1", (0...1).inspect) assert_equal("0...1", (0...1).inspect)
assert_equal("0..", (0..nil).inspect)
assert_equal("0...", (0...nil).inspect)
bug11767 = '[ruby-core:71811] [Bug #11767]' bug11767 = '[ruby-core:71811] [Bug #11767]'
assert_predicate(("0".taint.."1").inspect, :tainted?, bug11767) assert_predicate(("0".taint.."1").inspect, :tainted?, bug11767)