Return back legacy Range#step behavior for symbol ranges
This commit is contained in:
parent
155989415b
commit
d7b0f26963
Notes:
git
2024-09-09 08:46:31 +00:00
39
range.c
39
range.c
@ -309,8 +309,8 @@ range_each_func(VALUE range, int (*func)(VALUE, VALUE), VALUE arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NB: Two functions below (step_i_iter and step_i) are used only to maintain the
|
// NB: Two functions below (step_i_iter, sym_step_i and step_i) are used only to maintain the
|
||||||
// backward-compatible behavior for string ranges with integer steps. If that branch
|
// backward-compatible behavior for string and symbol ranges with integer steps. If that branch
|
||||||
// will be removed from range_step, these two can go, too.
|
// will be removed from range_step, these two can go, too.
|
||||||
static bool
|
static bool
|
||||||
step_i_iter(VALUE arg)
|
step_i_iter(VALUE arg)
|
||||||
@ -328,6 +328,15 @@ step_i_iter(VALUE arg)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
sym_step_i(VALUE i, VALUE arg)
|
||||||
|
{
|
||||||
|
if (step_i_iter(arg)) {
|
||||||
|
rb_yield(rb_str_intern(i));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
step_i(VALUE i, VALUE arg)
|
step_i(VALUE i, VALUE arg)
|
||||||
{
|
{
|
||||||
@ -482,15 +491,16 @@ range_step(int argc, VALUE *argv, VALUE range)
|
|||||||
|
|
||||||
const VALUE b_num_p = rb_obj_is_kind_of(b, rb_cNumeric);
|
const VALUE b_num_p = rb_obj_is_kind_of(b, rb_cNumeric);
|
||||||
const VALUE e_num_p = rb_obj_is_kind_of(e, rb_cNumeric);
|
const VALUE e_num_p = rb_obj_is_kind_of(e, rb_cNumeric);
|
||||||
// For backward compatibility reasons (conforming to behavior before 3.4), String supports
|
// For backward compatibility reasons (conforming to behavior before 3.4), String/Symbol
|
||||||
// both old behavior ('a'..).step(1) and new behavior ('a'..).step('a')
|
// supports both old behavior ('a'..).step(1) and new behavior ('a'..).step('a')
|
||||||
// Hence the additional conversion/addional checks.
|
// Hence the additional conversion/addional checks.
|
||||||
const VALUE sb = rb_check_string_type(b);
|
const VALUE str_b = rb_check_string_type(b);
|
||||||
|
const VALUE sym_b = SYMBOL_P(b) ? rb_sym2str(b) : Qnil;
|
||||||
|
|
||||||
if (rb_check_arity(argc, 0, 1))
|
if (rb_check_arity(argc, 0, 1))
|
||||||
step = argv[0];
|
step = argv[0];
|
||||||
else {
|
else {
|
||||||
if (b_num_p || !NIL_P(sb) || (NIL_P(b) && e_num_p))
|
if (b_num_p || !NIL_P(str_b) || !NIL_P(sym_b) || (NIL_P(b) && e_num_p))
|
||||||
step = INT2FIX(1);
|
step = INT2FIX(1);
|
||||||
else
|
else
|
||||||
rb_raise(rb_eArgError, "step is required for non-numeric ranges");
|
rb_raise(rb_eArgError, "step is required for non-numeric ranges");
|
||||||
@ -561,17 +571,28 @@ range_step(int argc, VALUE *argv, VALUE range)
|
|||||||
}
|
}
|
||||||
else if (b_num_p && step_num_p && ruby_float_step(b, e, step, EXCL(range), TRUE)) {
|
else if (b_num_p && step_num_p && ruby_float_step(b, e, step, EXCL(range), TRUE)) {
|
||||||
/* done */
|
/* done */
|
||||||
} else if (!NIL_P(sb) && FIXNUM_P(step)) {
|
} else if (!NIL_P(str_b) && FIXNUM_P(step)) {
|
||||||
// backwards compatibility behavior for String only, when no step/Integer step is passed
|
// backwards compatibility behavior for String only, when no step/Integer step is passed
|
||||||
// See discussion in https://bugs.ruby-lang.org/issues/18368
|
// See discussion in https://bugs.ruby-lang.org/issues/18368
|
||||||
|
|
||||||
VALUE iter[2] = {INT2FIX(1), step};
|
VALUE iter[2] = {INT2FIX(1), step};
|
||||||
|
|
||||||
if (NIL_P(e)) {
|
if (NIL_P(e)) {
|
||||||
rb_str_upto_endless_each(sb, step_i, (VALUE)iter);
|
rb_str_upto_endless_each(str_b, step_i, (VALUE)iter);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rb_str_upto_each(sb, e, EXCL(range), step_i, (VALUE)iter);
|
rb_str_upto_each(str_b, e, EXCL(range), step_i, (VALUE)iter);
|
||||||
|
}
|
||||||
|
} else if (!NIL_P(sym_b) && FIXNUM_P(step)) {
|
||||||
|
// same as above: backward compatibility for symbols
|
||||||
|
|
||||||
|
VALUE iter[2] = {INT2FIX(1), step};
|
||||||
|
|
||||||
|
if (NIL_P(e)) {
|
||||||
|
rb_str_upto_endless_each(sym_b, sym_step_i, (VALUE)iter);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rb_str_upto_each(sym_b, rb_sym2str(e), EXCL(range), sym_step_i, (VALUE)iter);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
v = b;
|
v = b;
|
||||||
|
@ -519,6 +519,58 @@ class TestRange < Test::Unit::TestCase
|
|||||||
assert_equal(%w[a b c], ('a'...).step.take(3))
|
assert_equal(%w[a b c], ('a'...).step.take(3))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_step_symbol_legacy
|
||||||
|
# finite
|
||||||
|
a = []
|
||||||
|
(:a..:g).step(2) { a << _1 }
|
||||||
|
assert_equal(%i[a c e g], a)
|
||||||
|
|
||||||
|
assert_kind_of(Enumerator, (:a..:g).step(2))
|
||||||
|
assert_equal(%i[a c e g], (:a..:g).step(2).to_a)
|
||||||
|
|
||||||
|
a = []
|
||||||
|
(:a...:g).step(2) { a << _1 }
|
||||||
|
assert_equal(%i[a c e], a)
|
||||||
|
|
||||||
|
assert_kind_of(Enumerator, (:a...:g).step(2))
|
||||||
|
assert_equal(%i[a c e], (:a...:g).step(2).to_a)
|
||||||
|
|
||||||
|
# endless
|
||||||
|
a = []
|
||||||
|
(:a...).step(2) { a << _1; break if a.size == 3 }
|
||||||
|
assert_equal(%i[a c e], a)
|
||||||
|
|
||||||
|
assert_kind_of(Enumerator, (:a...).step(2))
|
||||||
|
assert_equal(%i[a c e], (:a...).step(2).take(3))
|
||||||
|
|
||||||
|
# beginless
|
||||||
|
assert_raise(ArgumentError) { (...:g).step(2) {} }
|
||||||
|
assert_raise(ArgumentError) { (...:g).step(2) }
|
||||||
|
|
||||||
|
# step is not provided
|
||||||
|
a = []
|
||||||
|
(:a..:d).step { a << _1 }
|
||||||
|
assert_equal(%i[a b c d], a)
|
||||||
|
|
||||||
|
assert_kind_of(Enumerator, (:a..:d).step)
|
||||||
|
assert_equal(%i[a b c d], (:a..:d).step.to_a)
|
||||||
|
|
||||||
|
a = []
|
||||||
|
(:a...:d).step { a << _1 }
|
||||||
|
assert_equal(%i[a b c], a)
|
||||||
|
|
||||||
|
assert_kind_of(Enumerator, (:a...:d).step)
|
||||||
|
assert_equal(%i[a b c], (:a...:d).step.to_a)
|
||||||
|
|
||||||
|
# endless
|
||||||
|
a = []
|
||||||
|
(:a...).step { a << _1; break if a.size == 3 }
|
||||||
|
assert_equal(%i[a b c], a)
|
||||||
|
|
||||||
|
assert_kind_of(Enumerator, (:a...).step)
|
||||||
|
assert_equal(%i[a b c], (:a...).step.take(3))
|
||||||
|
end
|
||||||
|
|
||||||
def test_step_bug15537
|
def test_step_bug15537
|
||||||
assert_equal([10.0, 9.0, 8.0, 7.0], (10 ..).step(-1.0).take(4))
|
assert_equal([10.0, 9.0, 8.0, 7.0], (10 ..).step(-1.0).take(4))
|
||||||
assert_equal([10.0, 9.0, 8.0, 7.0], (10.0 ..).step(-1).take(4))
|
assert_equal([10.0, 9.0, 8.0, 7.0], (10.0 ..).step(-1).take(4))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user