From 8a4c9b0b77c1796deefeb41bbd5a79ef8c3bd63d Mon Sep 17 00:00:00 2001 From: nobu Date: Thu, 21 Jan 2010 02:15:48 +0000 Subject: * array.c (rb_ary_rotate): new methods, Array#rotate! and Array#rotate. [ruby-dev:17194] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@26366 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 5 ++- NEWS | 2 + array.c | 117 +++++++++++++++++++++++++++++++++++++++++++++--- test/ruby/test_array.rb | 47 +++++++++++++++++++ 4 files changed, 163 insertions(+), 8 deletions(-) diff --git a/ChangeLog b/ChangeLog index 13fa09497c..8b77736b63 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,7 @@ -Thu Jan 21 11:12:06 2010 Nobuyoshi Nakada +Thu Jan 21 11:15:46 2010 Nobuyoshi Nakada + + * array.c (rb_ary_rotate): new methods, Array#rotate! and + Array#rotate. [ruby-dev:17194] * array.c (rb_ary_reverse_m): copy directly. diff --git a/NEWS b/NEWS index 6dbf5dfba0..86e791a966 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,8 @@ with all sufficient information, see the ChangeLog file. * Array * new method: * Array#sort_by! + * Array#rotate! + * Array#rotate * Dir * new method: diff --git a/array.c b/array.c index ab89f5f30f..b844d0cdb7 100644 --- a/array.c +++ b/array.c @@ -1743,22 +1743,27 @@ rb_ary_to_ary_m(VALUE ary) 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 @@ rb_ary_reverse_m(VALUE ary) 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 @@ Init_Array(void) 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); diff --git a/test/ruby/test_array.rb b/test/ruby/test_array.rb index 7bfd9dde87..8c6fa8b4e1 100644 --- a/test/ruby/test_array.rb +++ b/test/ruby/test_array.rb @@ -1767,4 +1767,51 @@ class TestArray < Test::Unit::TestCase 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 -- cgit v1.2.3