/* * call-seq: * rng.step(n=1) {| obj | block } => rng * * Iterates over <i>rng</i>, passing each <i>n</i>th element to the block. If * the range contains numbers or strings, natural ordering is used. Otherwise * <code>step</code> invokes <code>succ</code> to iterate through range * elements. The following code uses class <code>Xs</code>, which is defined * in the class-level documentation. * * range = Xs.new(1)..Xs.new(10) * range.step(2) {|x| puts x} * range.step(3) {|x| puts x} * * <em>produces:</em> * * 1 x * 3 xxx * 5 xxxxx * 7 xxxxxxx * 9 xxxxxxxxx * 1 x * 4 xxxx * 7 xxxxxxx * 10 xxxxxxxxxx */ static VALUE range_step(argc, argv, range) int argc; VALUE *argv; VALUE range; { VALUE b, e, step; long unit; b = rb_ivar_get(range, id_beg); e = rb_ivar_get(range, id_end); if (rb_scan_args(argc, argv, "01", &step) == 0) { step = INT2FIX(1); } unit = NUM2LONG(step); if (unit < 0) { rb_raise(rb_eArgError, "step can't be negative"); } if (FIXNUM_P(b) && FIXNUM_P(e)) { /* fixnums are special */ long end = FIX2LONG(e); long i; if (unit == 0) rb_raise(rb_eArgError, "step can't be 0"); if (!EXCL(range)) end += 1; i = FIX2LONG(b); while (i < end) { rb_yield(LONG2NUM(i)); if (i + unit < i) break; i += unit; } } else { VALUE tmp = rb_check_string_type(b); if (!NIL_P(tmp)) { VALUE args[5]; long iter[2]; b = tmp; if (unit == 0) rb_raise(rb_eArgError, "step can't be 0"); args[0] = b; args[1] = e; args[2] = range; iter[0] = 1; iter[1] = unit; rb_iterate((VALUE(*)_((VALUE)))str_step, (VALUE)args, step_i, (VALUE)iter); } else if (rb_obj_is_kind_of(b, rb_cNumeric)) { ID c = rb_intern(EXCL(range) ? "<" : "<="); if (rb_equal(step, INT2FIX(0))) rb_raise(rb_eArgError, "step can't be 0"); while (RTEST(rb_funcall(b, c, 1, e))) { rb_yield(b); b = rb_funcall(b, '+', 1, step); } } else { long args[2]; if (unit == 0) rb_raise(rb_eArgError, "step can't be 0"); if (!rb_respond_to(b, id_succ)) { rb_raise(rb_eTypeError, "can't iterate from %s", rb_obj_classname(b)); } args[0] = 1; args[1] = unit; range_each_func(range, step_i, b, e, args); } } return range; }