[前][次][番号順一覧][スレッド一覧]

ruby-changes:17083

From: nobu <ko1@a...>
Date: Tue, 24 Aug 2010 07:08:41 +0900 (JST)
Subject: [ruby-changes:17083] Ruby:r29083 (trunk): * array.c (rb_ary_shuffle_bang, rb_ary_sample): add optional

nobu	2010-08-24 07:07:39 +0900 (Tue, 24 Aug 2010)

  New Revision: 29083

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=29083

  Log:
    * 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.

  Modified files:
    trunk/ChangeLog
    trunk/array.c
    trunk/random.c
    trunk/test/ruby/test_array.rb
    trunk/test/ruby/test_rand.rb

Index: array.c
===================================================================
--- array.c	(revision 29082)
+++ array.c	(revision 29083)
@@ -3730,22 +3730,30 @@
     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 @@
 
 /*
  *  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 @@
  *  contained duplicate elements. If the array is empty the first form returns
  *  <code>nil</code> 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 @@
 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 @@
     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 @@
     rb_define_method(rb_cArray, "drop_while", rb_ary_drop_while, 0);
 
     id_cmp = rb_intern("<=>");
+    sym_random = ID2SYM(rb_intern("random"));
 }
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 29082)
+++ ChangeLog	(revision 29083)
@@ -1,3 +1,11 @@
+Tue Aug 24 07:07:28 2010  Nobuyoshi Nakada  <nobu@r...>
+
+	* 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  <nobu@r...>
 
 	* include/ruby/version.h (RUBY_API_VERSION_*): renamed and moved
Index: test/ruby/test_array.rb
===================================================================
--- test/ruby/test_array.rb	(revision 29082)
+++ test/ruby/test_array.rb	(revision 29083)
@@ -1891,6 +1891,12 @@
     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 @@
     (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 @@
     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
Index: test/ruby/test_rand.rb
===================================================================
--- test/ruby/test_rand.rb	(revision 29082)
+++ test/ruby/test_rand.rb	(revision 29083)
@@ -171,6 +171,7 @@
   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
Index: random.c
===================================================================
--- random.c	(revision 29082)
+++ random.c	(revision 29083)
@@ -321,6 +321,7 @@
 VALUE rb_cRandom;
 #define id_minus '-'
 #define id_plus  '+'
+static ID id_rand, id_bytes;
 
 /* :nodoc: */
 static void
@@ -359,6 +360,13 @@
     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 @@
 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 @@
 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 @@
     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");
 }

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]