/* * call-seq: * num.step(limit, step ) {|i| block } => num * * Invokes <em>block</em> with the sequence of numbers starting at * <i>num</i>, incremented by <i>step</i> on each call. The loop * finishes when the value to be passed to the block is greater than * <i>limit</i> (if <i>step</i> is positive) or less than * <i>limit</i> (if <i>step</i> is negative). If all the arguments are * integers, the loop operates using an integer counter. If any of the * arguments are floating point numbers, all are converted to floats, * and the loop is executed <i>floor(n + n*epsilon)+ 1</i> times, * where <i>n = (limit - num)/step</i>. Otherwise, the loop * starts at <i>num</i>, uses either the <code><</code> or * <code>></code> operator to compare the counter against * <i>limit</i>, and increments itself using the <code>+</code> * operator. * * 1.step(10, 2) { |i| print i, " " } * Math::E.step(Math::PI, 0.2) { |f| print f, " " } * * <em>produces:</em> * * 1 3 5 7 9 * 2.71828182845905 2.91828182845905 3.11828182845905 */ static VALUE num_step(argc, argv, from) int argc; VALUE *argv; VALUE from; { VALUE to, step; if (argc == 1) { to = argv[0]; step = INT2FIX(1); } else { if (argc == 2) { to = argv[0]; step = argv[1]; } else { rb_raise(rb_eArgError, "wrong number of arguments"); } if (rb_equal(step, INT2FIX(0))) { rb_raise(rb_eArgError, "step can't be 0"); } } if (FIXNUM_P(from) && FIXNUM_P(to) && FIXNUM_P(step)) { long i, end, diff; i = FIX2LONG(from); end = FIX2LONG(to); diff = FIX2LONG(step); if (diff > 0) { while (i <= end) { rb_yield(LONG2FIX(i)); i += diff; } } else { while (i >= end) { rb_yield(LONG2FIX(i)); i += diff; } } } else if (TYPE(from) == T_FLOAT || TYPE(to) == T_FLOAT || TYPE(step) == T_FLOAT) { const double epsilon = DBL_EPSILON; double beg = NUM2DBL(from); double end = NUM2DBL(to); double unit = NUM2DBL(step); double n = (end - beg)/unit; double err = (fabs(beg) + fabs(end) + fabs(end-beg)) / fabs(unit) * epsilon; long i; if (err>0.5) err=0.5; n = floor(n + err) + 1; for (i=0; i<n; i++) { rb_yield(rb_float_new(i*unit+beg)); } } else { VALUE i = from; ID cmp; if (RTEST(rb_funcall(step, '>', 1, INT2FIX(0)))) { cmp = '>'; } else { cmp = '<'; } for (;;) { if (RTEST(rb_funcall(i, cmp, 1, to))) break; rb_yield(i); i = rb_funcall(i, '+', 1, step); } } return from; }