step(...)
public
Invokes block with the sequence of numbers starting at
num, incremented by step
on each call. The loop finishes when the value to be passed to the block is
greater than limit (if step is positive) or less than
limit (if step 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 floor(n + n*epsilon)+
1 times, where n = (limit - num)/step. Otherwise, the loop
starts at num, uses either the < or >
operator to compare the counter against limit, and increments
itself using the + operator.
1.step(10, 2) { |i| print i, " " }
Math::E.step(Math::PI, 0.2) { |f| print f, " " }
produces:
1 3 5 7 9
2.71828182845905 2.91828182845905 3.11828182845905
Show source
/*
* 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;
}