From: murphy Date: 2007-11-16T18:47:50+09:00 Subject: Array#split_at This is a multi-part message in MIME format. --------------080405080009010104090902 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Trans wrote: > On Nov 15, 4:49 am, murphy wrote: >> VALUE >> rb_ary_split_at(VALUE ary, VALUE pos) > This is nice. Add in an extra length option, and IMO it's a worthy of > consideration for core. I tried, and it's working fine with tests, but I have performance issues: short.each_with_index do |el, i| pre, post = short.split_at(i) end is always slower (!) than last = long.size - 1 short.each_with_index do |el, i| pre = short.first(i) post = short.last(last - i) end and I assume it's because of the parallel assignment. I'm using ary_shared_array just like #first and #last, rb_assoc_new for the result and avoid rb_scan_args. can anything more be done to make it faster? I'm still not sure about the name... [murphy] --------------080405080009010104090902 Content-Type: text/plain; x-mac-type="0"; x-mac-creator="0"; name="array_split_at_draft_1.diff" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="array_split_at_draft_1.diff" Index: array.c =================================================================== --- array.c (revision 13948) +++ array.c (working copy) @@ -781,6 +781,76 @@ return rb_ary_entry(ary, NUM2LONG(pos)); } +/* + * call-seq: + * array.split_at(index) -> [left_array, right_array] + * array.split_at(start, length) -> [left_array, right_array] + * + * Splits an array into two parts, with the first one containing + * all elements before the given range, and the second one containing + * all elements after it. See Array#slice for information about the + * range expressions. + * + * a = [ "a", "b", "c", "d", "e" ] + * a.split_at(0) #=> [[], ["b", "c", "d", "e"]] + * a.split_at(2) #=> [["a", "b"], ["d", "e"]] + * a.split_at(-1) #=> [["a", "b", "c", "d"], []] + * a.split_at(1, 2) #=> [["a"], ["d", "e"]] + * a.split_at(1, 0) #=> [["a"], ["b", "c", "d", "e"]] + * + */ + +VALUE +rb_ary_split_at(int argc, VALUE *argv, VALUE ary) +{ + return Qnil; + + long pos, len; + len = 1; /* default */ + + switch (argc) { + case 2: + len = NUM2LONG(argv[1]); + /* fall through */ + case 1: + pos = NUM2LONG(argv[0]); + break; + default: + rb_scan_args(argc, argv, "11", 0, 0); + } + + if (RARRAY_LEN(ary) == 0) { + return rb_assoc_new(rb_ary_new2(0), rb_ary_new2(0)); + } + + if (pos < 0) { + pos += RARRAY_LEN(ary); + } + + /* pos out of bounds? */ + if (pos <= 0) { + return rb_assoc_new(rb_ary_new2(0), ary_shared_array(rb_cArray, ary)); + } + + /* pos+len out of bounds? */ + if (len < 0) { + /* Array#slice doesn't fail on negative lengths, so we copy */ + len = 0; + } + if (pos + len > RARRAY_LEN(ary)) { + return rb_assoc_new(ary_shared_array(rb_cArray, ary), rb_ary_new2(0)); + } + + VALUE left = ary_shared_array(rb_cArray, ary); + VALUE right = ary_shared_array(rb_cArray, ary); + + RARRAY(left)->len = pos; + RARRAY(right)->ptr += pos + len; + RARRAY(right)->len += RARRAY_LEN(ary) - (pos + len); + + return rb_assoc_new(left, right); +} + /* * call-seq: * array.first -> obj or nil @@ -2557,7 +2627,7 @@ hash = ary_make_hash(ary2, 0); if (RHASH_EMPTY_P(hash)) - return ary3; + return ary3; for (i=0; i