/*
* 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;
}