Feature #16350 ยป 0001-Start-implementing-ArithmeticSequence-member.patch
enumerator.c | ||
---|---|---|
return str;
|
||
}
|
||
/*
|
||
* call-seq:
|
||
* arith_sequence.include?(obj) -> true or false
|
||
* arith_sequence.member?(obj) -> true or false
|
||
*
|
||
* Returns <code>true</code> if any member of <i>arith_sequence</i>
|
||
* equals <i>obj</i>. If <i>obj</i> is an integer and the beginning
|
||
* and step are integers, then this computes whether or not the
|
||
* sequence includes <i>obj</i> using arithmetic. If <i>obj</i> is a
|
||
* float, then TBD. (It might just fall back to the existing behavior
|
||
* of enumerating the sequence until the sequence ends or the number
|
||
* is found.)
|
||
*
|
||
* (1..10).include? 5 #=> true
|
||
* (1..10).include? 15 #=> false
|
||
* (1..10).member? 5 #=> true
|
||
* (1..10).member? 15 #=> false
|
||
*
|
||
*/
|
||
static VALUE
|
||
arith_seq_member_p(VALUE self, VALUE element)
|
||
{
|
||
VALUE b, s, offset, remainder;
|
||
b = arith_seq_begin(self);
|
||
if (!(CLASS_OF(b) == CLASS_OF(element))) {
|
||
return Qfalse;
|
||
}
|
||
s = arith_seq_step(self);
|
||
if ( (RB_FLOAT_TYPE_P(s) && FLOAT_ZERO_P(s)) ||
|
||
(RB_INTEGER_TYPE_P(s) && FIXNUM_ZERO_P(s))) {
|
||
if (element == b) return Qtrue;
|
||
return Qfalse;
|
||
}
|
||
/* TODO: Handle floats */
|
||
offset = rb_int_minus(element, b);
|
||
/* Determine if the sequence will be going away from the element,
|
||
* e.g. if the sequence is 1.step then no non-positive numbers
|
||
* will be in the sequence. TODO: Also check the end of the range,
|
||
* if it exists.
|
||
*/
|
||
if (FIXNUM_POSITIVE_P(offset)) {
|
||
if (!FIXNUM_POSITIVE_P(s)) {
|
||
return Qfalse;
|
||
}
|
||
} else {
|
||
if (FIXNUM_POSITIVE_P(s)) {
|
||
return Qfalse;
|
||
}
|
||
}
|
||
/* Test to see if the difference between the element between
|
||
* checked and the beginning of the sequence is divisible by the
|
||
* step of the sequence. If it is, then the element is included in
|
||
* the sequence. Otherwise, it's not.
|
||
*/
|
||
remainder = rb_int_modulo(offset, s);
|
||
if (FIXNUM_ZERO_P(remainder)) {
|
||
return Qtrue;
|
||
}
|
||
else {
|
||
return Qfalse;
|
||
}
|
||
}
|
||
/*
|
||
* call-seq:
|
||
* aseq == obj -> true or false
|
||
... | ... | |
rb_define_method(rb_cArithSeq, "hash", arith_seq_hash, 0);
|
||
rb_define_method(rb_cArithSeq, "each", arith_seq_each, 0);
|
||
rb_define_method(rb_cArithSeq, "size", arith_seq_size, 0);
|
||
rb_define_method(rb_cArithSeq, "member?", arith_seq_member_p, 1);
|
||
rb_define_method(rb_cArithSeq, "include?", arith_seq_member_p, 1);
|
||
rb_provide("enumerator.so"); /* for backward compatibility */
|
||
}
|
test/ruby/test_arithmetic_sequence.rb | ||
---|---|---|
assert_equal([1.0, 2.5, 4.0, 5.5, 7.0, 8.5, 10.0].sum, (1.0..10.0).step(1.5).sum)
|
||
assert_equal([1/2r, 1r, 3/2r, 2, 5/2r, 3, 7/2r, 4].sum, ((1/2r)...(9/2r)).step(1/2r).sum)
|
||
end
|
||
def test_member_p
|
||
assert_send([1.step, :member?, 10])
|
||
assert_not_send([1.step(by: 2), :member?, 10])
|
||
assert_send([1.step(by: 0), :member?, 1])
|
||
assert_not_send([1.step(by: 0), :member?, 2])
|
||
assert_send([1.step(by: -1), :member?, -5])
|
||
assert_not_send([1.step(by: -1), :member?, 5])
|
||
assert_send([(1..10).step, :member?, 6])
|
||
assert_not_send([(1..10).step(2), :member?, 6])
|
||
assert_not_send([(1..10).step, :member?, 11])
|
||
assert_not_send([(-5..5).step, :member? -10])
|
||
assert_send([1.5.step, :member?, 2.5])
|
||
assert_not_send([1.5.step, :member?, 3])
|
||
end
|
||
end
|