From: Yusuke ENDOH Date: 2007-11-10T01:43:04+09:00 Subject: [ruby-dev:32198] [提案] Array#tail 遠藤と申します。 先頭の要素以外の配列を返す Array#tail というメソッドを提案します。 [1, 2, 3, 4].tail #=> [2, 3, 4] 現状では ary[1..-1] がイディオムになっていると思いますが、 - インデックスに負の値を使っていてややトリッキー - (私の主観では) 見栄えもあまり良くない - そのわりにかなり頻出 なので、専用のメソッドで ary.tail と書けるといいと思います。 # 「Haskell 脳にやさしい」という効果もあります (?) String#[] なども含むので正確な値ではないですが、ruby のコード中だけでも 193 箇所ほどあります。 $ find . -name \*.rb | xargs grep "\[1\.\.-1\]" | wc -l 193 とりあえず実装してみました。 ついでに Array#head を first の alias にしています。 よろしくご検討下さい。 Index: array.c =================================================================== --- array.c (revision 13858) +++ array.c (working copy) @@ -785,6 +785,8 @@ * call-seq: * array.first -> obj or nil * array.first(n) -> an_array + * array.head -> obj or nil + * array.head(n) -> an_array * * Returns the first element, or the first +n+ elements, of the array. * If the array is empty, the first form returns nil, and the @@ -832,6 +834,48 @@ } } +/* call-seq: + * array.tail -> obj or nil + * array.tail(n) -> sub_array + * + * Returns the array without the first element, or the first +n+ elements, + * of self. If the array is empty, the first form returns + * nil, and the second form returns an empty array. + * Equivalent to: + * + * def tail(n = 1) + * raise ArgumentError, "negative array size" if n < 0 + * self[n..-1] || (n == 1 ? nil : []) + * end + * + * a = [ "q", "r", "s", "t" ] + * a.tail #=> ["r", "s", "t"] + * a.tail(2) #=> ["s", "t"] + * a.tail(5) #=> [] + * [].tail #=> nil + * [].tail(2) #=> [] + */ +static VALUE +rb_ary_tail(int argc, VALUE *argv, VALUE ary) +{ + VALUE len; + if (argc == 0) { + if (RARRAY_LEN(ary) == 0) return Qnil; + len = LONG2NUM(RARRAY_LEN(ary) - 1); + return ary_shared_first(1, &len, ary, Qtrue); + } + else if (NUM2LONG(argv[0]) < 0) { + rb_raise(rb_eArgError, "negative array size"); + } + else if (RARRAY_LEN(ary) < NUM2LONG(argv[0])) { + return rb_ary_new3(0); + } + else { + argv[0] = LONG2NUM(RARRAY_LEN(ary) - NUM2LONG(argv[0])); + return ary_shared_first(argc, argv, ary, Qtrue); + } +} + /* * call-seq: * array.fetch(index) -> obj @@ -3247,7 +3291,9 @@ rb_define_method(rb_cArray, "at", rb_ary_at, 1); rb_define_method(rb_cArray, "fetch", rb_ary_fetch, -1); rb_define_method(rb_cArray, "first", rb_ary_first, -1); + rb_define_alias(rb_cArray, "head", "first"); rb_define_method(rb_cArray, "last", rb_ary_last, -1); + rb_define_method(rb_cArray, "tail", rb_ary_tail, -1); rb_define_method(rb_cArray, "concat", rb_ary_concat, 1); rb_define_method(rb_cArray, "<<", rb_ary_push, 1); rb_define_method(rb_cArray, "push", rb_ary_push_m, -1); Index: test/ruby/test_array.rb =================================================================== --- test/ruby/test_array.rb (revision 13858) +++ test/ruby/test_array.rb (working copy) @@ -720,6 +720,18 @@ assert(a1.hash != a3.hash) end + def test_head + assert_equal(3, @cls[3, 4, 5].head) + assert_equal([], @cls[3, 4, 5].head(0)) + assert_equal([3], @cls[3, 4, 5].head(1)) + assert_equal([3, 4], @cls[3, 4, 5].head(2)) + assert_equal([3, 4, 5], @cls[3, 4, 5].head(3)) + assert_equal([3, 4, 5], @cls[3, 4, 5].head(4)) + assert_raise(ArgumentError) { @cls[3, 4, 5].head(-1) } + assert_equal(nil, @cls[].head) + assert_equal([], @cls[].head(1)) + end + def test_include? a = @cls[ 'cat', 99, /a/, @cls[ 1, 2, 3] ] assert(a.include?('cat')) @@ -1103,6 +1115,18 @@ assert_equal(@cls[], @cls[].sort!) end + def test_tail + assert_equal([4, 5], @cls[3, 4, 5].tail) + assert_equal([3, 4, 5], @cls[3, 4, 5].tail(0)) + assert_equal([4, 5], @cls[3, 4, 5].tail(1)) + assert_equal([5], @cls[3, 4, 5].tail(2)) + assert_equal([], @cls[3, 4, 5].tail(3)) + assert_equal([], @cls[3, 4, 5].tail(4)) + assert_raise(ArgumentError) { @cls[3, 4, 5].tail(-1) } + assert_equal(nil, @cls[].tail) + assert_equal([], @cls[].tail(1)) + end + def test_to_a a = @cls[ 1, 2, 3 ] a_id = a.__id__ -- Yusuke ENDOH