From 2f6c0e3be38b057239eb044426c152ac5633c88f Mon Sep 17 00:00:00 2001 From: nobu Date: Mon, 23 Aug 2010 22:07:39 +0000 Subject: * array.c (rb_ary_shuffle_bang, rb_ary_sample): add optional argument random. [ruby-dev:41923] [EXPERIMENTAL] * random.c (rb_random_{int32,real,bytes}): fallback to normal method invocation. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@29083 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 8 ++++++++ array.c | 45 +++++++++++++++++++++++++++++++++------------ random.c | 35 ++++++++++++++++++++++++++++++----- test/ruby/test_array.rb | 17 ++++++++++++++++- test/ruby/test_rand.rb | 1 + 5 files changed, 88 insertions(+), 18 deletions(-) diff --git a/ChangeLog b/ChangeLog index 04eb818ce7..ac55987353 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +Tue Aug 24 07:07:28 2010 Nobuyoshi Nakada + + * array.c (rb_ary_shuffle_bang, rb_ary_sample): add optional + argument random. [ruby-dev:41923] [EXPERIMENTAL] + + * random.c (rb_random_{int32,real,bytes}): fallback to normal + method invocation. + Tue Aug 24 06:08:10 2010 Nobuyoshi Nakada * include/ruby/version.h (RUBY_API_VERSION_*): renamed and moved diff --git a/array.c b/array.c index d047565022..172a653aa2 100644 --- a/array.c +++ b/array.c @@ -3730,22 +3730,30 @@ rb_ary_flatten(int argc, VALUE *argv, VALUE ary) return result; } -#define RAND_UPTO(max) (long)(rb_genrand_real()*(max)) +#define OPTHASH_GIVEN_P(opts) \ + (argc > 0 && !NIL_P(opts = rb_check_hash_type(argv[argc-1])) && (--argc, 1)) +static VALUE sym_random; + +#define RAND_UPTO(max) (long)(rb_random_real(randgen)*(max)) /* * call-seq: - * ary.shuffle! -> ary + * ary.shuffle! -> ary + * ary.shuffle!(random: rng) -> ary * * Shuffles elements in +self+ in place. + * If +rng+ is given, it will be used as the random number generator. */ - static VALUE -rb_ary_shuffle_bang(VALUE ary) +rb_ary_shuffle_bang(int argc, VALUE *argv, VALUE ary) { - VALUE *ptr; + VALUE *ptr, opts, randgen = Qnil; long i = RARRAY_LEN(ary); + if (OPTHASH_GIVEN_P(opts)) { + randgen = rb_hash_lookup2(opts, sym_random, randgen); + } rb_ary_modify(ary); ptr = RARRAY_PTR(ary); while (i) { @@ -3760,27 +3768,34 @@ rb_ary_shuffle_bang(VALUE ary) /* * call-seq: - * ary.shuffle -> new_ary + * ary.shuffle -> new_ary + * ary.shuffle(random: rng) -> new_ary * * Returns a new array with elements of this array shuffled. * * a = [ 1, 2, 3 ] #=> [1, 2, 3] * a.shuffle #=> [2, 3, 1] + * + * If +rng+ is given, it will be used as the random number generator. + * + * a.shuffle(Random.new(1)) #=> [1, 3, 2] */ static VALUE -rb_ary_shuffle(VALUE ary) +rb_ary_shuffle(int argc, VALUE *argv, VALUE ary) { ary = rb_ary_dup(ary); - rb_ary_shuffle_bang(ary); + rb_ary_shuffle_bang(argc, argv, ary); return ary; } /* * call-seq: - * ary.sample -> obj - * ary.sample(n) -> new_ary + * ary.sample -> obj + * ary.sample(random: rng) -> obj + * ary.sample(n) -> new_ary + * ary.sample(n, random: rng) -> new_ary * * Choose a random element or +n+ random elements from the array. The elements * are chosen by using random and unique indices into the array in order to @@ -3788,6 +3803,7 @@ rb_ary_shuffle(VALUE ary) * contained duplicate elements. If the array is empty the first form returns * nil and the second form returns an empty array. * + * If +rng+ is given, it will be used as the random number generator. */ @@ -3795,9 +3811,13 @@ static VALUE rb_ary_sample(int argc, VALUE *argv, VALUE ary) { VALUE nv, result, *ptr; + VALUE opts, randgen = Qnil; long n, len, i, j, k, idx[10]; len = RARRAY_LEN(ary); + if (OPTHASH_GIVEN_P(opts)) { + randgen = rb_hash_lookup2(opts, sym_random, randgen); + } if (argc == 0) { if (len == 0) return Qnil; i = len == 1 ? 0 : RAND_UPTO(len); @@ -4595,8 +4615,8 @@ Init_Array(void) rb_define_method(rb_cArray, "flatten", rb_ary_flatten, -1); rb_define_method(rb_cArray, "flatten!", rb_ary_flatten_bang, -1); rb_define_method(rb_cArray, "count", rb_ary_count, -1); - rb_define_method(rb_cArray, "shuffle!", rb_ary_shuffle_bang, 0); - rb_define_method(rb_cArray, "shuffle", rb_ary_shuffle, 0); + rb_define_method(rb_cArray, "shuffle!", rb_ary_shuffle_bang, -1); + rb_define_method(rb_cArray, "shuffle", rb_ary_shuffle, -1); rb_define_method(rb_cArray, "sample", rb_ary_sample, -1); rb_define_method(rb_cArray, "cycle", rb_ary_cycle, -1); rb_define_method(rb_cArray, "permutation", rb_ary_permutation, -1); @@ -4611,4 +4631,5 @@ Init_Array(void) rb_define_method(rb_cArray, "drop_while", rb_ary_drop_while, 0); id_cmp = rb_intern("<=>"); + sym_random = ID2SYM(rb_intern("random")); } diff --git a/random.c b/random.c index ef0de1f750..a89fcb8ee9 100644 --- a/random.c +++ b/random.c @@ -321,6 +321,7 @@ int_pair_to_real_inclusive(unsigned int a, unsigned int b) VALUE rb_cRandom; #define id_minus '-' #define id_plus '+' +static ID id_rand, id_bytes; /* :nodoc: */ static void @@ -359,6 +360,13 @@ get_rnd(VALUE obj) return ptr; } +static rb_random_t * +try_get_rnd(VALUE obj) +{ + if (!rb_typeddata_is_kind_of(obj, &random_data_type)) return NULL; + return DATA_PTR(obj); +} + /* :nodoc: */ static VALUE random_alloc(VALUE klass) @@ -869,14 +877,22 @@ rb_rand_internal(unsigned long i) unsigned int rb_random_int32(VALUE obj) { - rb_random_t *rnd = get_rnd(obj); + rb_random_t *rnd = try_get_rnd(obj); + if (!rnd) { + VALUE lim = ULONG2NUM(0xffffffff); + return NUM2ULONG(rb_funcall2(obj, id_rand, 1, &lim)); + } return genrand_int32(&rnd->mt); } double rb_random_real(VALUE obj) { - rb_random_t *rnd = get_rnd(obj); + rb_random_t *rnd = try_get_rnd(obj); + if (!rnd) { + VALUE v = rb_funcall2(obj, id_rand, 0, 0); + return NUM2DBL(v); + } return genrand_real(&rnd->mt); } @@ -895,11 +911,17 @@ random_bytes(VALUE obj, VALUE len) VALUE rb_random_bytes(VALUE obj, long n) { - rb_random_t *rnd = get_rnd(obj); - VALUE bytes = rb_str_new(0, n); - char *ptr = RSTRING_PTR(bytes); + rb_random_t *rnd = try_get_rnd(obj); + VALUE bytes; + char *ptr; unsigned int r, i; + if (!rnd) { + VALUE len = LONG2NUM(n); + return rb_funcall2(obj, id_bytes, 1, &len); + } + bytes = rb_str_new(0, n); + ptr = RSTRING_PTR(bytes); for (; n >= SIZEOF_INT32; n -= SIZEOF_INT32) { r = genrand_int32(&rnd->mt); i = SIZEOF_INT32; @@ -1245,4 +1267,7 @@ Init_Random(void) rb_define_singleton_method(rb_cRandom, "new_seed", random_seed, 0); rb_define_private_method(CLASS_OF(rb_cRandom), "state", random_s_state, 0); rb_define_private_method(CLASS_OF(rb_cRandom), "left", random_s_left, 0); + + id_rand = rb_intern("rand"); + id_bytes = rb_intern("bytes"); } diff --git a/test/ruby/test_array.rb b/test/ruby/test_array.rb index 3e32175012..f95d8870a1 100644 --- a/test/ruby/test_array.rb +++ b/test/ruby/test_array.rb @@ -1891,6 +1891,12 @@ class TestArray < Test::Unit::TestCase 100.times do assert_equal([0, 1, 2], [2, 1, 0].shuffle.sort) end + + gen = Random.new(0) + srand(0) + 100.times do + assert_equal([0, 1, 2].shuffle, [0, 1, 2].shuffle(random: gen)) + end end def test_sample @@ -1907,7 +1913,7 @@ class TestArray < Test::Unit::TestCase (0..20).each do |n| 100.times do b = a.sample(n) - assert_equal([n, 18].min, b.uniq.size) + assert_equal([n, 18].min, b.size) assert_equal(a, (a | b).sort) assert_equal(b.sort, (a & b).sort) end @@ -1920,6 +1926,15 @@ class TestArray < Test::Unit::TestCase end assert_raise(ArgumentError, '[ruby-core:23374]') {[1, 2].sample(-1)} + + gen = Random.new(0) + srand(0) + a = (1..18).to_a + (0..20).each do |n| + 100.times do |i| + assert_equal(a.sample(n), a.sample(n, random: gen), "#{i}/#{n}") + end + end end def test_cycle diff --git a/test/ruby/test_rand.rb b/test/ruby/test_rand.rb index 40b291f959..c3282cf804 100644 --- a/test/ruby/test_rand.rb +++ b/test/ruby/test_rand.rb @@ -171,6 +171,7 @@ class TestRand < Test::Unit::TestCase def test_shuffle srand(0) assert_equal([1,4,2,5,3], [1,2,3,4,5].shuffle) + assert_equal([1,4,2,5,3], [1,2,3,4,5].shuffle(random: Random.new(0))) end def test_big_seed -- cgit v1.2.3