diff options
author | Victor Shepelev <[email protected]> | 2024-08-18 13:15:18 +0300 |
---|---|---|
committer | <[email protected]> | 2024-08-18 13:15:18 +0300 |
commit | d450f9d6a28f01b7ca6030a925921dbf35cee439 () | |
tree | 287feda2769e22ddc5e698ca55a993e3472b2a0d /range.c | |
parent | 4dbf386ca248df0f47f31dc28cdeabe8d4477e5b (diff) |
Make Range#step to consistently use + for iteration (#7444)
Make Range#step to consistently use + for iteration [Feature #18368] Previously, non-numerics expected step to be integer, and iterated with begin#succ, skipping over step value steps. Since this commit, numeric and non-numeric iteration behaves the same way, by using + operator.
Notes: Merged-By: zverok <[email protected]>
-rw-r--r-- | range.c | 309 |
1 files changed, 164 insertions, 145 deletions
@@ -29,7 +29,7 @@ #include "internal/range.h" VALUE rb_cRange; -static ID id_beg, id_end, id_excl; #define id_cmp idCmp #define id_succ idSucc #define id_min idMin @@ -308,40 +308,6 @@ range_each_func(VALUE range, int (*func)(VALUE, VALUE), VALUE arg) } } -static bool -step_i_iter(VALUE arg) -{ - VALUE *iter = (VALUE *)arg; - - if (FIXNUM_P(iter[0])) { - iter[0] -= INT2FIX(1) & ~FIXNUM_FLAG; - } - else { - iter[0] = rb_funcall(iter[0], '-', 1, INT2FIX(1)); - } - if (iter[0] != INT2FIX(0)) return false; - iter[0] = iter[1]; - 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 -step_i(VALUE i, VALUE arg) -{ - if (step_i_iter(arg)) { - rb_yield(i); - } - return 0; -} - static int discrete_object_p(VALUE obj) { @@ -400,72 +366,123 @@ range_step_size(VALUE range, VALUE args, VALUE eobj) /* * call-seq: - * step(n = 1) {|element| ... } -> self - * step(n = 1) -> enumerator * - * Iterates over the elements of +self+. * - * With a block given and no argument, - * calls the block each element of the range; returns +self+: * - * a = [] - * (1..5).step {|element| a.push(element) } # => 1..5 - * a # => [1, 2, 3, 4, 5] - * a = [] - * ('a'..'e').step {|element| a.push(element) } # => "a".."e" - * a # => ["a", "b", "c", "d", "e"] * - * With a block given and a positive integer argument +n+ given, - * calls the block with element +0+, element +n+, element <tt>2n</tt>, and so on: * - * a = [] - * (1..5).step(2) {|element| a.push(element) } # => 1..5 - * a # => [1, 3, 5] - * a = [] - * ('a'..'e').step(2) {|element| a.push(element) } # => "a".."e" - * a # => ["a", "c", "e"] * - * With no block given, returns an enumerator, - * which will be of class Enumerator::ArithmeticSequence if +self+ is numeric; - * otherwise of class Enumerator: * - * e = (1..5).step(2) # => ((1..5).step(2)) - * e.class # => Enumerator::ArithmeticSequence - * ('a'..'e').step # => #<Enumerator: ...> * - * Related: Range#%. */ static VALUE range_step(int argc, VALUE *argv, VALUE range) { - VALUE b, e, step, tmp; b = RANGE_BEG(range); e = RANGE_END(range); - step = (!rb_check_arity(argc, 0, 1) ? INT2FIX(1) : argv[0]); - if (!rb_block_given_p()) { - if (!rb_obj_is_kind_of(step, rb_cNumeric)) { - step = rb_to_int(step); - } - if (rb_equal(step, INT2FIX(0))) { - rb_raise(rb_eArgError, "step can't be 0"); - } - 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); - if ((b_num_p && (NIL_P(e) || e_num_p)) || (NIL_P(b) && e_num_p)) { return rb_arith_seq_new(range, ID2SYM(rb_frame_this_func()), argc, argv, range_step_size, b, e, step, EXCL(range)); } - RETURN_SIZED_ENUMERATOR(range, argc, argv, range_step_size); } - step = check_step_domain(step); - VALUE iter[2] = {INT2FIX(1), step}; if (FIXNUM_P(b) && NIL_P(e) && FIXNUM_P(step)) { long i = FIX2LONG(b), unit = FIX2LONG(step); do { rb_yield(LONG2FIX(i)); @@ -473,71 +490,77 @@ range_step(int argc, VALUE *argv, VALUE range) } while (FIXABLE(i)); b = LONG2NUM(i); for (;; b = rb_big_plus(b, step)) rb_yield(b); } - else if (FIXNUM_P(b) && FIXNUM_P(e) && FIXNUM_P(step)) { /* fixnums are special */ long end = FIX2LONG(e); long i, unit = FIX2LONG(step); - if (!EXCL(range)) - end += 1; - i = FIX2LONG(b); - while (i < end) { - rb_yield(LONG2NUM(i)); - if (i + unit < i) break; - i += unit; - } - - } - else if (SYMBOL_P(b) && (NIL_P(e) || SYMBOL_P(e))) { /* symbols are special */ - b = rb_sym2str(b); - if (NIL_P(e)) { - rb_str_upto_endless_each(b, sym_step_i, (VALUE)iter); - } - else { - rb_str_upto_each(b, rb_sym2str(e), EXCL(range), sym_step_i, (VALUE)iter); } } - else if (ruby_float_step(b, e, step, EXCL(range), TRUE)) { /* done */ } - else if (rb_obj_is_kind_of(b, rb_cNumeric) || - !NIL_P(rb_check_to_integer(b, "to_int")) || - !NIL_P(rb_check_to_integer(e, "to_int"))) { - ID op = EXCL(range) ? '<' : idLE; - VALUE v = b; - int i = 0; - - while (NIL_P(e) || RTEST(rb_funcall(v, op, 1, e))) { - rb_yield(v); - i++; - v = rb_funcall(b, '+', 1, rb_funcall(INT2NUM(i), '*', 1, step)); - } - } else { - tmp = rb_check_string_type(b); - if (!NIL_P(tmp)) { - b = tmp; - if (NIL_P(e)) { - rb_str_upto_endless_each(b, step_i, (VALUE)iter); - } - else { - rb_str_upto_each(b, e, EXCL(range), step_i, (VALUE)iter); } } - else { - if (!discrete_object_p(b)) { - rb_raise(rb_eTypeError, "can't iterate from %s", - rb_obj_classname(b)); - } - if (!NIL_P(e)) - range_each_func(range, step_i, (VALUE)iter); - else - for (;; b = rb_funcallv(b, id_succ, 0, 0)) - step_i(b, (VALUE)iter); - } } return range; } @@ -545,29 +568,24 @@ range_step(int argc, VALUE *argv, VALUE range) /* * call-seq: * %(n) {|element| ... } -> self - * %(n) -> enumerator - * - * Iterates over the elements of +self+. * - * With a block given, calls the block with selected elements of the range; - * returns +self+: * - * a = [] - * (1..5).%(2) {|element| a.push(element) } # => 1..5 - * a # => [1, 3, 5] - * a = [] - * ('a'..'e').%(2) {|element| a.push(element) } # => "a".."e" - * a # => ["a", "c", "e"] * - * With no block given, returns an enumerator, - * which will be of class Enumerator::ArithmeticSequence if +self+ is numeric; - * otherwise of class Enumerator: * - * e = (1..5) % 2 # => ((1..5).%(2)) - * e.class # => Enumerator::ArithmeticSequence - * ('a'..'e') % 2 # => #<Enumerator: ...> * - * Related: Range#step. */ static VALUE range_percent_step(VALUE range, VALUE step) @@ -2641,6 +2659,7 @@ Init_Range(void) id_beg = rb_intern_const("begin"); id_end = rb_intern_const("end"); id_excl = rb_intern_const("excl"); rb_cRange = rb_struct_define_without_accessor( "Range", rb_cObject, range_alloc, |