class Enumerator::ArithmeticSequence - RDoc Documentation (original) (raw)

Enumerator::ArithmeticSequence is a subclass of Enumerator, that is a representation of sequences of numbers with common difference. Instances of this class can be generated by the Range#step and Numeric#step methods.

The class can be used for slicing Array (see Array#slice) or custom collections.

Public Instance Methods

aseq == obj → true or false click to toggle source

Returns true only if obj is an Enumerator::ArithmeticSequence, has equivalent begin, end, step, and exclude_end? settings.

static VALUE arith_seq_eq(VALUE self, VALUE other) { if (!RTEST(rb_obj_is_kind_of(other, rb_cArithSeq))) { return Qfalse; }

if (!rb_equal(arith_seq_begin(self), arith_seq_begin(other))) {
    return Qfalse;
}

if (!rb_equal(arith_seq_end(self), arith_seq_end(other))) {
    return Qfalse;
}

if (!rb_equal(arith_seq_step(self), arith_seq_step(other))) {
    return Qfalse;
}

if (arith_seq_exclude_end_p(self) != arith_seq_exclude_end_p(other)) {
    return Qfalse;
}

return Qtrue;

}

begin() click to toggle source

inline VALUE arith_seq_begin(VALUE self) { return rb_ivar_get(self, id_begin); }

each {|i| block } → aseq click to toggle source

each → aseq

static VALUE arith_seq_each(VALUE self) { VALUE c, e, s, len_1, last; int x;

if (!rb_block_given_p()) return self;

c = arith_seq_begin(self);
e = arith_seq_end(self);
s = arith_seq_step(self);
x = arith_seq_exclude_end_p(self);

if (!RB_TYPE_P(s, T_COMPLEX) && ruby_float_step(c, e, s, x, TRUE)) {
    return self;
}

if (NIL_P(e)) {
    while (1) {
        rb_yield(c);
        c = rb_int_plus(c, s);
    }

    return self;
}

if (rb_equal(s, INT2FIX(0))) {
    while (1) {
        rb_yield(c);
    }

    return self;
}

len_1 = num_idiv(num_minus(e, c), s);
last = num_plus(c, num_mul(s, len_1));
if (x && rb_equal(last, e)) {
    last = num_minus(last, s);
}

if (rb_num_negative_int_p(s)) {
    while (NUM_GE(c, last)) {
        rb_yield(c);
        c = num_plus(c, s);
    }
}
else {
    while (NUM_GE(last, c)) {
        rb_yield(c);
        c = num_plus(c, s);
    }
}

return self;

}

end() click to toggle source

inline VALUE arith_seq_end(VALUE self) { return rb_ivar_get(self, id_end); }

exclude_end?() click to toggle source

inline VALUE arith_seq_exclude_end(VALUE self) { return rb_ivar_get(self, id_exclude_end); }

first → num or nil click to toggle source

first(n) → an_array

Returns the first number in this arithmetic sequence, or an array of the first n elements.

static VALUE arith_seq_first(int argc, VALUE *argv, VALUE self) { VALUE b, e, s, ary; long n; int x;

rb_check_arity(argc, 0, 1);

b = arith_seq_begin(self);
e = arith_seq_end(self);
s = arith_seq_step(self);
if (argc == 0) {
    if (NIL_P(b)) {
        return Qnil;
    }
    if (!NIL_P(e)) {
        VALUE zero = INT2FIX(0);
        int r = rb_cmpint(rb_num_coerce_cmp(s, zero, idCmp), s, zero);
        if (r > 0 && RTEST(rb_funcall(b, '>', 1, e))) {
            return Qnil;
        }
        if (r < 0 && RTEST(rb_funcall(b, '<', 1, e))) {
            return Qnil;
        }
    }
    return b;
}

// TODO: the following code should be extracted as arith_seq_take

n = NUM2LONG(argv[0]);
if (n < 0) {
    rb_raise(rb_eArgError, "attempt to take negative size");
}
if (n == 0) {
    return rb_ary_new_capa(0);
}

x = arith_seq_exclude_end_p(self);

if (FIXNUM_P(b) && NIL_P(e) && FIXNUM_P(s)) {
    long i = FIX2LONG(b), unit = FIX2LONG(s);
    ary = rb_ary_new_capa(n);
    while (n > 0 && FIXABLE(i)) {
        rb_ary_push(ary, LONG2FIX(i));
        i += unit;  // FIXABLE + FIXABLE never overflow;
        --n;
    }
    if (n > 0) {
        b = LONG2NUM(i);
        while (n > 0) {
            rb_ary_push(ary, b);
            b = rb_big_plus(b, s);
            --n;
        }
    }
    return ary;
}
else if (FIXNUM_P(b) && FIXNUM_P(e) && FIXNUM_P(s)) {
    long i = FIX2LONG(b);
    long end = FIX2LONG(e);
    long unit = FIX2LONG(s);
    long len;

    if (unit >= 0) {
        if (!x) end += 1;

        len = end - i;
        if (len < 0) len = 0;
        ary = rb_ary_new_capa((n < len) ? n : len);
        while (n > 0 && i < end) {
            rb_ary_push(ary, LONG2FIX(i));
            if (i + unit < i) break;
            i += unit;
            --n;
        }
    }
    else {
        if (!x) end -= 1;

        len = i - end;
        if (len < 0) len = 0;
        ary = rb_ary_new_capa((n < len) ? n : len);
        while (n > 0 && i > end) {
            rb_ary_push(ary, LONG2FIX(i));
            if (i + unit > i) break;
            i += unit;
            --n;
        }
    }
    return ary;
}
else if (RB_FLOAT_TYPE_P(b) || RB_FLOAT_TYPE_P(e) || RB_FLOAT_TYPE_P(s)) {
    /* generate values like ruby_float_step */

    double unit = NUM2DBL(s);
    double beg = NUM2DBL(b);
    double end = NIL_P(e) ? (unit < 0 ? -1 : 1)*HUGE_VAL : NUM2DBL(e);
    double len = ruby_float_step_size(beg, end, unit, x);
    long i;

    if (n > len)
        n = (long)len;

    if (isinf(unit)) {
        if (len > 0) {
            ary = rb_ary_new_capa(1);
            rb_ary_push(ary, DBL2NUM(beg));
        }
        else {
            ary = rb_ary_new_capa(0);
        }
    }
    else if (unit == 0) {
        VALUE val = DBL2NUM(beg);
        ary = rb_ary_new_capa(n);
        for (i = 0; i < len; ++i) {
            rb_ary_push(ary, val);
        }
    }
    else {
        ary = rb_ary_new_capa(n);
        for (i = 0; i < n; ++i) {
            double d = i*unit+beg;
            if (unit >= 0 ? end < d : d < end) d = end;
            rb_ary_push(ary, DBL2NUM(d));
        }
    }

    return ary;
}

return rb_call_super(argc, argv);

}

hash → integer click to toggle source

Compute a hash-value for this arithmetic sequence. Two arithmetic sequences with same begin, end, step, and exclude_end? values will generate the same hash-value.

See also Object#hash.

static VALUE arith_seq_hash(VALUE self) { st_index_t hash; VALUE v;

hash = rb_hash_start(arith_seq_exclude_end_p(self));
v = rb_hash(arith_seq_begin(self));
hash = rb_hash_uint(hash, NUM2LONG(v));
v = rb_hash(arith_seq_end(self));
hash = rb_hash_uint(hash, NUM2LONG(v));
v = rb_hash(arith_seq_step(self));
hash = rb_hash_uint(hash, NUM2LONG(v));
hash = rb_hash_end(hash);

return ST2FIX(hash);

}

inspect → string click to toggle source

Convert this arithmetic sequence to a printable form.

static VALUE arith_seq_inspect(VALUE self) { struct enumerator *e; VALUE eobj, str, eargs; int range_p;

TypedData_Get_Struct(self, struct enumerator, &enumerator_data_type, e);

eobj = rb_attr_get(self, id_receiver);
if (NIL_P(eobj)) {
    eobj = e->obj;
}

range_p = RTEST(rb_obj_is_kind_of(eobj, rb_cRange));
str = rb_sprintf("(%s%"PRIsVALUE"%s.", range_p ? "(" : "", eobj, range_p ? ")" : "");

rb_str_buf_append(str, rb_id2str(e->meth));

eargs = rb_attr_get(eobj, id_arguments);
if (NIL_P(eargs)) {
    eargs = e->args;
}
if (eargs != Qfalse) {
    long argc = RARRAY_LEN(eargs);
    const VALUE *argv = RARRAY_CONST_PTR(eargs); /* WB: no new reference */

    if (argc > 0) {
        VALUE kwds = Qnil;

        rb_str_buf_cat2(str, "(");

        if (RB_TYPE_P(argv[argc-1], T_HASH)) {
            int all_key = TRUE;
            rb_hash_foreach(argv[argc-1], key_symbol_p, (VALUE)&all_key);
            if (all_key) kwds = argv[--argc];
        }

        while (argc--) {
            VALUE arg = *argv++;

            rb_str_append(str, rb_inspect(arg));
            rb_str_buf_cat2(str, ", ");
        }
        if (!NIL_P(kwds)) {
            rb_hash_foreach(kwds, kwd_append, str);
        }
        rb_str_set_len(str, RSTRING_LEN(str)-2); /* drop the last ", " */
        rb_str_buf_cat2(str, ")");
    }
}

rb_str_buf_cat2(str, ")");

return str;

}

last → num or nil click to toggle source

last(n) → an_array

Returns the last number in this arithmetic sequence, or an array of the last n elements.

static VALUE arith_seq_last(int argc, VALUE *argv, VALUE self) { VALUE b, e, s, len_1, len, last, nv, ary; int last_is_adjusted; long n;

e = arith_seq_end(self);
if (NIL_P(e)) {
    rb_raise(rb_eRangeError,
             "cannot get the last element of endless arithmetic sequence");
}

b = arith_seq_begin(self);
s = arith_seq_step(self);

len_1 = num_idiv(num_minus(e, b), s);
if (rb_num_negative_int_p(len_1)) {
    if (argc == 0) {
        return Qnil;
    }
    return rb_ary_new_capa(0);
}

last = num_plus(b, num_mul(s, len_1));
if ((last_is_adjusted = arith_seq_exclude_end_p(self) && rb_equal(last, e))) {
    last = num_minus(last, s);
}

if (argc == 0) {
    return last;
}

if (last_is_adjusted) {
    len = len_1;
}
else {
    len = rb_int_plus(len_1, INT2FIX(1));
}

rb_scan_args(argc, argv, "1", &nv);
if (!RB_INTEGER_TYPE_P(nv)) {
    nv = rb_to_int(nv);
}
if (RTEST(rb_int_gt(nv, len))) {
    nv = len;
}
n = NUM2LONG(nv);
if (n < 0) {
    rb_raise(rb_eArgError, "negative array size");
}

ary = rb_ary_new_capa(n);
b = rb_int_minus(last, rb_int_mul(s, nv));
while (n) {
    b = rb_int_plus(b, s);
    rb_ary_push(ary, b);
    --n;
}

return ary;

}

size → num or nil click to toggle source

Returns the number of elements in this arithmetic sequence if it is a finite sequence. Otherwise, returns nil.

static VALUE arith_seq_size(VALUE self) { VALUE b, e, s, len_1, len, last; int x;

b = arith_seq_begin(self);
e = arith_seq_end(self);
s = arith_seq_step(self);
x = arith_seq_exclude_end_p(self);

if (RB_FLOAT_TYPE_P(b) || RB_FLOAT_TYPE_P(e) || RB_FLOAT_TYPE_P(s)) {
    double ee, n;

    if (NIL_P(e)) {
        if (rb_num_negative_int_p(s)) {
            ee = -HUGE_VAL;
        }
        else {
            ee = HUGE_VAL;
        }
    }
    else {
        ee = NUM2DBL(e);
    }

    n = ruby_float_step_size(NUM2DBL(b), ee, NUM2DBL(s), x);
    if (isinf(n)) return DBL2NUM(n);
    if (POSFIXABLE(n)) return LONG2FIX((long)n);
    return rb_dbl2big(n);
}

if (NIL_P(e)) {
    return DBL2NUM(HUGE_VAL);
}

if (!rb_obj_is_kind_of(s, rb_cNumeric)) {
    s = rb_to_int(s);
}

if (rb_equal(s, INT2FIX(0))) {
    return DBL2NUM(HUGE_VAL);
}

len_1 = rb_int_idiv(rb_int_minus(e, b), s);
if (rb_num_negative_int_p(len_1)) {
    return INT2FIX(0);
}

last = rb_int_plus(b, rb_int_mul(s, len_1));
if (x && rb_equal(last, e)) {
    len = len_1;
}
else {
    len = rb_int_plus(len_1, INT2FIX(1));
}

return len;

}

step() click to toggle source

inline VALUE arith_seq_step(VALUE self) { return rb_ivar_get(self, id_step); }