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

ruby-changes:14529

From: nobu <ko1@a...>
Date: Thu, 21 Jan 2010 11:16:05 +0900 (JST)
Subject: [ruby-changes:14529] Ruby:r26366 (trunk): * array.c (rb_ary_rotate): new methods, Array#rotate! and

nobu	2010-01-21 11:15:48 +0900 (Thu, 21 Jan 2010)

  New Revision: 26366

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

  Log:
    * array.c (rb_ary_rotate): new methods, Array#rotate! and
      Array#rotate.  [ruby-dev:17194]

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

Index: array.c
===================================================================
--- array.c	(revision 26365)
+++ array.c	(revision 26366)
@@ -1743,22 +1743,27 @@
     return ary;
 }
 
+static void
+ary_reverse(p1, p2)
+    VALUE *p1, *p2;
+{
+    while (p1 < p2) {
+	VALUE tmp = *p1;
+	*p1++ = *p2;
+	*p2-- = tmp;
+    }
+}
+
 VALUE
 rb_ary_reverse(VALUE ary)
 {
     VALUE *p1, *p2;
-    VALUE tmp;
 
     rb_ary_modify(ary);
     if (RARRAY_LEN(ary) > 1) {
 	p1 = RARRAY_PTR(ary);
 	p2 = p1 + RARRAY_LEN(ary) - 1;	/* points last item */
-
-	while (p1 < p2) {
-	    tmp = *p1;
-	    *p1++ = *p2;
-	    *p2-- = tmp;
-	}
+	ary_reverse(p1, p2);
     }
     return ary;
 }
@@ -1804,6 +1809,102 @@
     return dup;
 }
 
+static inline long
+rotate_count(long cnt, long len)
+{
+    return (cnt < 0) ? (len - (~cnt % len) - 1) : (cnt % len);
+}
+
+VALUE
+rb_ary_rotate(VALUE ary, long cnt)
+{
+    rb_ary_modify(ary);
+
+    if (cnt != 0) {
+	VALUE *ptr = RARRAY_PTR(ary);
+	long len = RARRAY_LEN(ary);
+
+	if (len > 0 && (cnt = rotate_count(cnt, len)) > 0) {
+	    --len;
+	    if (cnt < len) ary_reverse(ptr + cnt, ptr + len);
+	    if (--cnt > 0) ary_reverse(ptr, ptr + cnt);
+	    if (len > 0) ary_reverse(ptr, ptr + len);
+	    return ary;
+	}
+    }
+
+    return Qnil;
+}
+    
+/*
+ *  call-seq:
+ *     array.rotate!([cnt = 1]) -> array
+ *
+ *  Rotates _self_ in place so that the element at +cnt+ comes first,
+ *  and returns _self_.  If +cnt+ is negative then it rotates in
+ *  counter direction.
+ *
+ *     a = [ "a", "b", "c", "d" ]
+ *     a.rotate!        #=> ["b", "c", "d", "a"]
+ *     a                #=> ["b", "c", "d", "a"]
+ *     a.rotate!(2)     #=> ["d", "a", "b", "c"]
+ *     a.rotate!(-3)    #=> ["a", "b", "c", "d"]
+ */
+
+static VALUE
+rb_ary_rotate_bang(int argc, VALUE *argv, VALUE ary)
+{
+    long n = 1;
+
+    switch (argc) {
+      case 1: n = NUM2LONG(argv[0]);
+      case 0: break;
+      default: rb_scan_args(argc, argv, "01", NULL);
+    }
+    rb_ary_rotate(ary, n);
+    return ary;
+}
+
+/*
+ *  call-seq:
+ *     array.rotate([n = 1]) -> an_array
+ *
+ *  Returns new array by rotating _self_, whose first element is the
+ *  element at +cnt+ in _self_.  If +cnt+ is negative then it rotates
+ *  in counter direction.
+ *
+ *     a = [ "a", "b", "c", "d" ]
+ *     a.rotate         #=> ["b", "c", "d", "a"]
+ *     a                #=> ["a", "b", "c", "d"]
+ *     a.rotate(2)      #=> ["c", "d", "a", "b"]
+ *     a.rotate(-3)     #=> ["b", "c", "d", "a"]
+ */
+
+static VALUE
+rb_ary_rotate_m(int argc, VALUE *argv, VALUE ary)
+{
+    VALUE rotated, *ptr, *ptr2;
+    long len, cnt = 1;
+
+    switch (argc) {
+      case 1: cnt = NUM2LONG(argv[0]);
+      case 0: break;
+      default: rb_scan_args(argc, argv, "01", NULL);
+    }
+
+    len = RARRAY_LEN(ary);
+    rotated = rb_ary_dup_setup(ary);
+    if (len > 0) {
+	cnt = rotate_count(cnt, len);
+	ptr = RARRAY_PTR(ary);
+	ptr2 = RARRAY_PTR(rotated);
+	len -= cnt;
+	MEMCPY(ptr2, ptr + cnt, VALUE, len);
+	MEMCPY(ptr2 + len, ptr, VALUE, cnt);
+    }
+    return rotated;
+}
+
 struct ary_sort_data {
     VALUE ary;
     int opt_methods;
@@ -4111,6 +4212,8 @@
     rb_define_method(rb_cArray, "join", rb_ary_join_m, -1);
     rb_define_method(rb_cArray, "reverse", rb_ary_reverse_m, 0);
     rb_define_method(rb_cArray, "reverse!", rb_ary_reverse_bang, 0);
+    rb_define_method(rb_cArray, "rotate", rb_ary_rotate_m, -1);
+    rb_define_method(rb_cArray, "rotate!", rb_ary_rotate_bang, -1);
     rb_define_method(rb_cArray, "sort", rb_ary_sort, 0);
     rb_define_method(rb_cArray, "sort!", rb_ary_sort_bang, 0);
     rb_define_method(rb_cArray, "sort_by!", rb_ary_sort_by_bang, 0);
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 26365)
+++ ChangeLog	(revision 26366)
@@ -1,5 +1,8 @@
-Thu Jan 21 11:12:06 2010  Nobuyoshi Nakada  <nobu@r...>
+Thu Jan 21 11:15:46 2010  Nobuyoshi Nakada  <nobu@r...>
 
+	* array.c (rb_ary_rotate): new methods, Array#rotate! and
+	  Array#rotate.  [ruby-dev:17194]
+
 	* array.c (rb_ary_reverse_m): copy directly.
 
 Thu Jan 21 09:38:00 2010  Nobuyoshi Nakada  <nobu@r...>
Index: NEWS
===================================================================
--- NEWS	(revision 26365)
+++ NEWS	(revision 26366)
@@ -15,6 +15,8 @@
   * Array
     * new method:
       * Array#sort_by!
+      * Array#rotate!
+      * Array#rotate
 
   * Dir
     * new method:
Index: test/ruby/test_array.rb
===================================================================
--- test/ruby/test_array.rb	(revision 26365)
+++ test/ruby/test_array.rb	(revision 26366)
@@ -1767,4 +1767,51 @@
     a.sort_by! {|x| -x }
     assert_equal([5,4,3,2,1], a)
   end
+
+  def test_rotate
+    a = [1,2,3,4,5].freeze
+    assert_equal([2,3,4,5,1], a.rotate)
+    assert_equal([5,1,2,3,4], a.rotate(-1))
+    assert_equal([3,4,5,1,2], a.rotate(2))
+    assert_equal([4,5,1,2,3], a.rotate(-2))
+    assert_equal([4,5,1,2,3], a.rotate(13))
+    assert_equal([3,4,5,1,2], a.rotate(-13))
+    a = [1].freeze
+    assert_equal([1], a.rotate)
+    assert_equal([1], a.rotate(2))
+    assert_equal([1], a.rotate(-4))
+    assert_equal([1], a.rotate(13))
+    assert_equal([1], a.rotate(-13))
+    a = [].freeze
+    assert_equal([], a.rotate)
+    assert_equal([], a.rotate(2))
+    assert_equal([], a.rotate(-4))
+    assert_equal([], a.rotate(13))
+    assert_equal([], a.rotate(-13))
+  end
+
+  def test_rotate!
+    a = [1,2,3,4,5]
+    assert_equal([2,3,4,5,1], a.rotate!)
+    assert_equal([2,3,4,5,1], a)
+    assert_equal([4,5,1,2,3], a.rotate!(2))
+    assert_equal([5,1,2,3,4], a.rotate!(-4))
+    assert_equal([3,4,5,1,2], a.rotate!(13))
+    assert_equal([5,1,2,3,4], a.rotate!(-13))
+    a = [1]
+    assert_equal([1], a.rotate!)
+    assert_equal([1], a.rotate!(2))
+    assert_equal([1], a.rotate!(-4))
+    assert_equal([1], a.rotate!(13))
+    assert_equal([1], a.rotate!(-13))
+    a = []
+    assert_equal([], a.rotate!)
+    assert_equal([], a.rotate!(2))
+    assert_equal([], a.rotate!(-4))
+    assert_equal([], a.rotate!(13))
+    assert_equal([], a.rotate!(-13))
+    a = [].freeze
+    e = assert_raise(RuntimeError) {a.rotate!}
+    assert_match(/can't modify frozen array/, e.message)
+  end
 end

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

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