class Enumerator::ArithmeticSequence
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
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; }
Returns the number that defines the first element of this arithmetic sequence.
static inline VALUE arith_seq_begin(VALUE self) { return rb_ivar_get(self, id_begin); }
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; }
Returns the number that defines the end of this arithmetic sequence.
static inline VALUE arith_seq_end(VALUE self) { return rb_ivar_get(self, id_end); }
Returns true
if this arithmetic sequence excludes its end value.
static inline VALUE arith_seq_exclude_end(VALUE self) { return rb_ivar_get(self, id_exclude_end); }
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); }
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); }
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; }
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; }
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; }
Returns the number that defines the common difference between two adjacent elements in this arithmetic sequence.
static inline VALUE arith_seq_step(VALUE self) { return rb_ivar_get(self, id_step); }