mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Enhance Numeric#step.
* numeric.c (num_step): Default the limit argument to infinity and allow it to be omitted. Keyword arguments (by: and to:) are introduced for ease of use. [Feature #8838] [ruby-dev:47662] [ruby-dev:42194] * numeric.c (num_step): Optimize for infinite loop. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@42781 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
a94833eb50
commit
fd4b5b8dc8
4 changed files with 141 additions and 67 deletions
|
@ -1,3 +1,12 @@
|
||||||
|
Mon Sep 2 23:46:29 2013 Akinori MUSHA <knu@iDaemons.org>
|
||||||
|
|
||||||
|
* numeric.c (num_step): Default the limit argument to infinity and
|
||||||
|
allow it to be omitted. Keyword arguments (by: and to:) are
|
||||||
|
introduced for ease of use. [Feature #8838] [ruby-dev:47662]
|
||||||
|
[ruby-dev:42194]
|
||||||
|
|
||||||
|
* numeric.c (num_step): Optimize for infinite loop.
|
||||||
|
|
||||||
Mon Sep 2 23:46:10 2013 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
Mon Sep 2 23:46:10 2013 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||||
|
|
||||||
* parse.y (parser_str_options): use valid suffix word only, as well as
|
* parse.y (parser_str_options): use valid suffix word only, as well as
|
||||||
|
|
6
NEWS
6
NEWS
|
@ -72,6 +72,12 @@ with all sufficient information, see the ChangeLog file.
|
||||||
* misc
|
* misc
|
||||||
* Mutex#owned? is no longer experimental.
|
* Mutex#owned? is no longer experimental.
|
||||||
|
|
||||||
|
* Numeric
|
||||||
|
* extended methods:
|
||||||
|
* Numeric#step allows the limit argument to be omitted, in which
|
||||||
|
case an infinite sequence of numbers is generated. Keyword
|
||||||
|
arguments `to` and `by` are introduced for ease of use.
|
||||||
|
|
||||||
* Process
|
* Process
|
||||||
* New methods:
|
* New methods:
|
||||||
* alternative methods to $0/$0=:
|
* alternative methods to $0/$0=:
|
||||||
|
|
116
numeric.c
116
numeric.c
|
@ -115,6 +115,8 @@ VALUE rb_cFixnum;
|
||||||
VALUE rb_eZeroDivError;
|
VALUE rb_eZeroDivError;
|
||||||
VALUE rb_eFloatDomainError;
|
VALUE rb_eFloatDomainError;
|
||||||
|
|
||||||
|
static VALUE sym_to, sym_by;
|
||||||
|
|
||||||
void
|
void
|
||||||
rb_num_zerodiv(void)
|
rb_num_zerodiv(void)
|
||||||
{
|
{
|
||||||
|
@ -1844,24 +1846,59 @@ ruby_num_interval_step_size(VALUE from, VALUE to, VALUE step, int excl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define NUM_STEP_SCAN_ARGS(argc, argv, to, step, hash, desc, inf) do { \
|
||||||
|
argc = rb_scan_args(argc, argv, "02:", &to, &step, &hash); \
|
||||||
|
if (!NIL_P(hash)) { \
|
||||||
|
step = rb_hash_aref(hash, sym_by); \
|
||||||
|
to = rb_hash_aref(hash, sym_to); \
|
||||||
|
} \
|
||||||
|
else { \
|
||||||
|
/* compatibility */ \
|
||||||
|
if (rb_equal(step, INT2FIX(0))) { \
|
||||||
|
rb_raise(rb_eArgError, "step can't be 0"); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
if (NIL_P(step)) step = INT2FIX(1); \
|
||||||
|
desc = negative_int_p(step); \
|
||||||
|
if (NIL_P(to)) to = desc ? DBL2NUM(-INFINITY) : DBL2NUM(INFINITY); \
|
||||||
|
if (TYPE(to) == T_FLOAT) { \
|
||||||
|
double f = RFLOAT_VALUE(to); \
|
||||||
|
inf = isinf(f) && (signbit(f) ? desc : !desc); \
|
||||||
|
} \
|
||||||
|
else inf = 0; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
num_step_size(VALUE from, VALUE args, VALUE eobj)
|
num_step_size(VALUE from, VALUE args, VALUE eobj)
|
||||||
{
|
{
|
||||||
VALUE to = RARRAY_AREF(args, 0);
|
VALUE to, step, hash;
|
||||||
VALUE step = (RARRAY_LEN(args) > 1) ? RARRAY_AREF(args, 1) : INT2FIX(1);
|
int desc, inf;
|
||||||
|
int argc = args ? RARRAY_LENINT(args) : 0;
|
||||||
|
VALUE *argv = args ? RARRAY_PTR(args) : 0;
|
||||||
|
|
||||||
|
NUM_STEP_SCAN_ARGS(argc, argv, to, step, hash, desc, inf);
|
||||||
|
|
||||||
return ruby_num_interval_step_size(from, to, step, FALSE);
|
return ruby_num_interval_step_size(from, to, step, FALSE);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* call-seq:
|
* call-seq:
|
||||||
* num.step(limit[, step]) {|i| block } -> self
|
* num.step(by: step, to: limit]) {|i| block } -> self
|
||||||
* num.step(limit[, step]) -> an_enumerator
|
* num.step(by: step, to: limit]) -> an_enumerator
|
||||||
|
* num.step(limit=nil, step=1) {|i| block } -> self
|
||||||
|
* num.step(limit=nil, step=1) -> an_enumerator
|
||||||
*
|
*
|
||||||
* Invokes the given block with the sequence of numbers starting at +num+,
|
* Invokes the given block with the sequence of numbers starting at +num+,
|
||||||
* incremented by +step+ (defaulted to +1+) on each call.
|
* incremented by +step+ (defaulted to +1+) on each call.
|
||||||
*
|
*
|
||||||
* The loop finishes when the value to be passed to the block is greater than
|
* 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
|
* +limit+ (if +step+ is positive) or less than +limit+ (if +step+ is
|
||||||
* negative).
|
* negative), where <i>limit</i> is defaulted to infinity.
|
||||||
|
*
|
||||||
|
* In the recommended keyword argument style, either or both of
|
||||||
|
* +step+ and +limit+ (default infinity) can be omitted. In the
|
||||||
|
* fixed position argument style, integer zero as a step
|
||||||
|
* (i.e. num.step(limit, 0)) is not allowed for historical
|
||||||
|
* compatibility reasons.
|
||||||
*
|
*
|
||||||
* If all the arguments are integers, the loop operates using an integer
|
* If all the arguments are integers, the loop operates using an integer
|
||||||
* counter.
|
* counter.
|
||||||
|
@ -1882,11 +1919,17 @@ num_step_size(VALUE from, VALUE args, VALUE eobj)
|
||||||
*
|
*
|
||||||
* For example:
|
* For example:
|
||||||
*
|
*
|
||||||
|
* p 1.step.take(4)
|
||||||
|
* p 10.step(by: -1).take(4)
|
||||||
|
* 3.step(to: 5) { |i| print i, " " }
|
||||||
* 1.step(10, 2) { |i| print i, " " }
|
* 1.step(10, 2) { |i| print i, " " }
|
||||||
* Math::E.step(Math::PI, 0.2) { |f| print f, " " }
|
* Math::E.step(to: Math::PI, by: 0.2) { |f| print f, " " }
|
||||||
*
|
*
|
||||||
* Will produce:
|
* Will produce:
|
||||||
*
|
*
|
||||||
|
* [1, 2, 3, 4]
|
||||||
|
* [10, 9, 8, 7]
|
||||||
|
* 3 4 5
|
||||||
* 1 3 5 7 9
|
* 1 3 5 7 9
|
||||||
* 2.71828182845905 2.91828182845905 3.11828182845905
|
* 2.71828182845905 2.91828182845905 3.11828182845905
|
||||||
*/
|
*/
|
||||||
|
@ -1894,56 +1937,46 @@ num_step_size(VALUE from, VALUE args, VALUE eobj)
|
||||||
static VALUE
|
static VALUE
|
||||||
num_step(int argc, VALUE *argv, VALUE from)
|
num_step(int argc, VALUE *argv, VALUE from)
|
||||||
{
|
{
|
||||||
VALUE to, step;
|
VALUE to, step, hash;
|
||||||
|
int desc, inf;
|
||||||
|
|
||||||
RETURN_SIZED_ENUMERATOR(from, argc, argv, num_step_size);
|
RETURN_SIZED_ENUMERATOR(from, argc, argv, num_step_size);
|
||||||
if (argc == 1) {
|
|
||||||
to = argv[0];
|
|
||||||
step = INT2FIX(1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
rb_check_arity(argc, 1, 2);
|
|
||||||
to = argv[0];
|
|
||||||
step = argv[1];
|
|
||||||
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)) {
|
NUM_STEP_SCAN_ARGS(argc, argv, to, step, hash, desc, inf);
|
||||||
long i, end, diff;
|
|
||||||
|
|
||||||
i = FIX2LONG(from);
|
if (FIXNUM_P(from) && (inf || FIXNUM_P(to)) && FIXNUM_P(step)) {
|
||||||
end = FIX2LONG(to);
|
long i = FIX2LONG(from);
|
||||||
diff = FIX2LONG(step);
|
long diff = FIX2LONG(step);
|
||||||
|
|
||||||
if (diff > 0) {
|
if (inf) {
|
||||||
while (i <= end) {
|
for (;; i += diff)
|
||||||
rb_yield(LONG2FIX(i));
|
rb_yield(LONG2FIX(i));
|
||||||
i += diff;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
while (i >= end) {
|
long end = FIX2LONG(to);
|
||||||
rb_yield(LONG2FIX(i));
|
|
||||||
i += diff;
|
if (desc) {
|
||||||
|
for (; i >= end; i += diff)
|
||||||
|
rb_yield(LONG2FIX(i));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (; i <= end; i += diff)
|
||||||
|
rb_yield(LONG2FIX(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!ruby_float_step(from, to, step, FALSE)) {
|
else if (!ruby_float_step(from, to, step, FALSE)) {
|
||||||
VALUE i = from;
|
VALUE i = from;
|
||||||
ID cmp;
|
|
||||||
|
|
||||||
if (positive_int_p(step)) {
|
if (inf) {
|
||||||
cmp = '>';
|
for (;; i = rb_funcall(i, '+', 1, step))
|
||||||
|
rb_yield(i);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
cmp = '<';
|
ID cmp = desc ? '<' : '>';
|
||||||
}
|
|
||||||
for (;;) {
|
for (; !RTEST(rb_funcall(i, cmp, 1, to)); i = rb_funcall(i, '+', 1, step))
|
||||||
if (RTEST(rb_funcall(i, cmp, 1, to))) break;
|
rb_yield(i);
|
||||||
rb_yield(i);
|
|
||||||
i = rb_funcall(i, '+', 1, step);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return from;
|
return from;
|
||||||
|
@ -4041,4 +4074,7 @@ Init_Numeric(void)
|
||||||
rb_define_method(rb_cFloat, "nan?", flo_is_nan_p, 0);
|
rb_define_method(rb_cFloat, "nan?", flo_is_nan_p, 0);
|
||||||
rb_define_method(rb_cFloat, "infinite?", flo_is_infinite_p, 0);
|
rb_define_method(rb_cFloat, "infinite?", flo_is_infinite_p, 0);
|
||||||
rb_define_method(rb_cFloat, "finite?", flo_is_finite_p, 0);
|
rb_define_method(rb_cFloat, "finite?", flo_is_finite_p, 0);
|
||||||
|
|
||||||
|
sym_to = ID2SYM(rb_intern("to"));
|
||||||
|
sym_by = ID2SYM(rb_intern("by"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -193,41 +193,64 @@ class TestNumeric < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def assert_step(expected, (from, *args), inf: false)
|
||||||
|
enum = from.step(*args)
|
||||||
|
size = enum.size
|
||||||
|
xsize = expected.size
|
||||||
|
|
||||||
|
if inf
|
||||||
|
assert_send [size, :infinite?], "step size: +infinity"
|
||||||
|
assert_send [size, :>, 0], "step size: +infinity"
|
||||||
|
|
||||||
|
a = []
|
||||||
|
from.step(*args) { |x| a << x; break if a.size == xsize }
|
||||||
|
assert_equal expected, a, "step"
|
||||||
|
|
||||||
|
a = []
|
||||||
|
enum.each { |x| a << x; break if a.size == xsize }
|
||||||
|
assert_equal expected, a, "step enumerator"
|
||||||
|
else
|
||||||
|
assert_equal expected.size, size, "step size"
|
||||||
|
|
||||||
|
a = []
|
||||||
|
from.step(*args) { |x| a << x }
|
||||||
|
assert_equal expected, a, "step"
|
||||||
|
|
||||||
|
a = []
|
||||||
|
enum.each { |x| a << x }
|
||||||
|
assert_equal expected, a, "step enumerator"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_step
|
def test_step
|
||||||
a = []
|
|
||||||
1.step(10) {|x| a << x }
|
|
||||||
assert_equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], a)
|
|
||||||
|
|
||||||
a = []
|
|
||||||
1.step(10, 2) {|x| a << x }
|
|
||||||
assert_equal([1, 3, 5, 7, 9], a)
|
|
||||||
|
|
||||||
assert_raise(ArgumentError) { 1.step(10, 1, 0) { } }
|
assert_raise(ArgumentError) { 1.step(10, 1, 0) { } }
|
||||||
|
assert_raise(ArgumentError) { 1.step(10, 1, 0).size }
|
||||||
assert_raise(ArgumentError) { 1.step(10, 0) { } }
|
assert_raise(ArgumentError) { 1.step(10, 0) { } }
|
||||||
|
assert_raise(ArgumentError) { 1.step(10, 0).size }
|
||||||
|
assert_nothing_raised { 1.step(by: 0) }
|
||||||
|
assert_nothing_raised { 1.step(by: 0).size }
|
||||||
|
|
||||||
a = []
|
assert_step [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 10]
|
||||||
10.step(1, -2) {|x| a << x }
|
assert_step [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, to: 10]
|
||||||
assert_equal([10, 8, 6, 4, 2], a)
|
assert_step [1, 3, 5, 7, 9], [1, 10, 2]
|
||||||
|
assert_step [1, 3, 5, 7, 9], [1, to: 10, by: 2]
|
||||||
|
|
||||||
a = []
|
assert_step [10, 8, 6, 4, 2], [10, 1, -2]
|
||||||
1.0.step(10.0, 2.0) {|x| a << x }
|
assert_step [10, 8, 6, 4, 2], [10, to: 1, by: -2]
|
||||||
assert_equal([1.0, 3.0, 5.0, 7.0, 9.0], a)
|
assert_step [1.0, 3.0, 5.0, 7.0, 9.0], [1.0, 10.0, 2.0]
|
||||||
|
assert_step [1.0, 3.0, 5.0, 7.0, 9.0], [1.0, to: 10.0, by: 2.0]
|
||||||
|
assert_step [1], [1, 10, 2**32]
|
||||||
|
assert_step [1], [1, to: 10, by: 2**32]
|
||||||
|
|
||||||
a = []
|
assert_step [3, 3, 3, 3], [3, by: 0], inf: true
|
||||||
1.step(10, 2**32) {|x| a << x }
|
assert_step [10], [10, 1, -(2**32)]
|
||||||
assert_equal([1], a)
|
|
||||||
|
|
||||||
a = []
|
assert_step [], [1, 0, Float::INFINITY]
|
||||||
10.step(1, -(2**32)) {|x| a << x }
|
assert_step [], [0, 1, -Float::INFINITY]
|
||||||
assert_equal([10], a)
|
assert_step [10], [10, to: 1, by: -(2**32)]
|
||||||
|
|
||||||
a = []
|
assert_step [10, 11, 12, 13], [10], inf: true
|
||||||
1.step(0, Float::INFINITY) {|x| a << x }
|
assert_step [10, 9, 8, 7], [10, by: -1], inf: true
|
||||||
assert_equal([], a)
|
|
||||||
|
|
||||||
a = []
|
|
||||||
0.step(1, -Float::INFINITY) {|x| a << x }
|
|
||||||
assert_equal([], a)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_num2long
|
def test_num2long
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue