diff options
Diffstat (limited to 'array.c')
| -rw-r--r-- | array.c | 4577 |
1 files changed, 2957 insertions, 1620 deletions
@@ -10,16 +10,29 @@ Copyright (C) 2000 Information-technology Promotion Agency, Japan **********************************************************************/ + +#include "debug_counter.h" +#include "id.h" +#include "internal.h" +#include "internal/array.h" +#include "internal/compar.h" +#include "internal/enum.h" +#include "internal/gc.h" +#include "internal/hash.h" +#include "internal/numeric.h" +#include "internal/object.h" +#include "internal/proc.h" +#include "internal/rational.h" +#include "internal/vm.h" +#include "probes.h" #include "ruby/encoding.h" -#include "ruby/util.h" #include "ruby/st.h" -#include "probes.h" -#include "id.h" -#include "debug_counter.h" +#include "ruby/util.h" #include "transient_heap.h" -#include "internal.h" +#include "builtin.h" #if !ARRAY_DEBUG +# undef NDEBUG # define NDEBUG #endif #include "ruby_assert.h" @@ -33,12 +46,14 @@ VALUE rb_cArray; #define ARY_MAX_SIZE (LONG_MAX / (int)sizeof(VALUE)) #define SMALL_ARRAY_LEN 16 +RBIMPL_ATTR_MAYBE_UNUSED() static int should_be_T_ARRAY(VALUE ary) { return RB_TYPE_P(ary, T_ARRAY); } +RBIMPL_ATTR_MAYBE_UNUSED() static int should_not_be_shared_and_embedded(VALUE ary) { @@ -204,9 +219,11 @@ ary_verify_(VALUE ary, const char *file, int line) #endif } +#if USE_TRANSIENT_HEAP if (RARRAY_TRANSIENT_P(ary)) { assert(rb_transient_heap_managed_ptr_p(RARRAY_CONST_PTR_TRANSIENT(ary))); } +#endif rb_transient_heap_verify(); @@ -337,14 +354,16 @@ ary_heap_free(VALUE ary) } } -static void +static size_t ary_heap_realloc(VALUE ary, size_t new_capa) { + size_t alloc_capa = new_capa; size_t old_capa = ARY_HEAP_CAPA(ary); if (RARRAY_TRANSIENT_P(ary)) { if (new_capa <= old_capa) { /* do nothing */ + alloc_capa = old_capa; } else { VALUE *new_ptr = rb_transient_heap_alloc(ary, sizeof(VALUE) * new_capa); @@ -362,6 +381,8 @@ ary_heap_realloc(VALUE ary, size_t new_capa) SIZED_REALLOC_N(RARRAY(ary)->as.heap.ptr, VALUE, new_capa, old_capa); } ary_verify(ary); + + return alloc_capa; } #if USE_TRANSIENT_HEAP @@ -426,6 +447,7 @@ ary_resize_capa(VALUE ary, long capacity) assert(!ARY_SHARED_P(ary)); if (capacity > RARRAY_EMBED_LEN_MAX) { + size_t new_capa = capacity; if (ARY_EMBED_P(ary)) { long len = ARY_EMBED_LEN(ary); VALUE *ptr = ary_heap_alloc(ary, capacity); @@ -436,9 +458,9 @@ ary_resize_capa(VALUE ary, long capacity) ARY_SET_HEAP_LEN(ary, len); } else { - ary_heap_realloc(ary, capacity); + new_capa = ary_heap_realloc(ary, capacity); } - ARY_SET_CAPA(ary, capacity); + ARY_SET_CAPA(ary, new_capa); } else { if (!ARY_EMBED_P(ary)) { @@ -492,13 +514,9 @@ rb_ary_decrement_share(VALUE shared_root) { if (shared_root) { long num = ARY_SHARED_ROOT_REFCNT(shared_root) - 1; - if (num == 0) { - rb_ary_free(shared_root); - rb_gc_force_recycle(shared_root); - } - else if (num > 0) { + if (num > 0) { ARY_SET_SHARED_ROOT_REFCNT(shared_root, num); - } + } } } @@ -545,34 +563,33 @@ rb_ary_modify_check(VALUE ary) } void -rb_ary_modify(VALUE ary) +rb_ary_cancel_sharing(VALUE ary) { - rb_ary_modify_check(ary); if (ARY_SHARED_P(ary)) { - long shared_len, len = RARRAY_LEN(ary); + long shared_len, len = RARRAY_LEN(ary); VALUE shared_root = ARY_SHARED_ROOT(ary); ary_verify(shared_root); if (len <= RARRAY_EMBED_LEN_MAX) { - const VALUE *ptr = ARY_HEAP_PTR(ary); + const VALUE *ptr = ARY_HEAP_PTR(ary); FL_UNSET_SHARED(ary); FL_SET_EMBED(ary); - MEMCPY((VALUE *)ARY_EMBED_PTR(ary), ptr, VALUE, len); + MEMCPY((VALUE *)ARY_EMBED_PTR(ary), ptr, VALUE, len); rb_ary_decrement_share(shared_root); ARY_SET_EMBED_LEN(ary, len); } else if (ARY_SHARED_ROOT_OCCUPIED(shared_root) && len > ((shared_len = RARRAY_LEN(shared_root))>>1)) { long shift = RARRAY_CONST_PTR_TRANSIENT(ary) - RARRAY_CONST_PTR_TRANSIENT(shared_root); - FL_UNSET_SHARED(ary); + FL_UNSET_SHARED(ary); ARY_SET_PTR(ary, RARRAY_CONST_PTR_TRANSIENT(shared_root)); - ARY_SET_CAPA(ary, shared_len); + ARY_SET_CAPA(ary, shared_len); RARRAY_PTR_USE_TRANSIENT(ary, ptr, { - MEMMOVE(ptr, ptr+shift, VALUE, len); - }); + MEMMOVE(ptr, ptr+shift, VALUE, len); + }); FL_SET_EMBED(shared_root); rb_ary_decrement_share(shared_root); - } + } else { VALUE *ptr = ary_heap_alloc(ary, len); MEMCPY(ptr, ARY_HEAP_PTR(ary), VALUE, len); @@ -581,11 +598,18 @@ rb_ary_modify(VALUE ary) ARY_SET_PTR(ary, ptr); } - rb_gc_writebarrier_remember(ary); + rb_gc_writebarrier_remember(ary); } ary_verify(ary); } +void +rb_ary_modify(VALUE ary) +{ + rb_ary_modify_check(ary); + rb_ary_cancel_sharing(ary); +} + static VALUE ary_ensure_room_for_push(VALUE ary, long add_len) { @@ -636,12 +660,15 @@ ary_ensure_room_for_push(VALUE ary, long add_len) /* * call-seq: - * ary.freeze -> ary + * array.freeze -> self * - * Calls Object#freeze on +ary+ to prevent any further - * modification. A RuntimeError will be raised if a modification - * attempt is made. + * Freezes +self+; returns +self+: + * a = [] + * a.frozen? # => false + * a.freeze + * a.frozen? # => true * + * An attempt to modify a frozen \Array raises FrozenError. */ VALUE @@ -764,6 +791,58 @@ rb_ary_new_from_values(long n, const VALUE *elts) return rb_ary_tmp_new_from_values(rb_cArray, n, elts); } +static VALUE +ec_ary_alloc(rb_execution_context_t *ec, VALUE klass) +{ + RB_EC_NEWOBJ_OF(ec, ary, struct RArray, klass, T_ARRAY | RARRAY_EMBED_FLAG | (RGENGC_WB_PROTECTED_ARRAY ? FL_WB_PROTECTED : 0)); + /* Created array is: + * FL_SET_EMBED((VALUE)ary); + * ARY_SET_EMBED_LEN((VALUE)ary, 0); + */ + return (VALUE)ary; +} + +static VALUE +ec_ary_new(rb_execution_context_t *ec, VALUE klass, long capa) +{ + VALUE ary,*ptr; + + if (capa < 0) { + rb_raise(rb_eArgError, "negative array size (or size too big)"); + } + if (capa > ARY_MAX_SIZE) { + rb_raise(rb_eArgError, "array size too big"); + } + + RUBY_DTRACE_CREATE_HOOK(ARRAY, capa); + + ary = ec_ary_alloc(ec, klass); + + if (capa > RARRAY_EMBED_LEN_MAX) { + ptr = ary_heap_alloc(ary, capa); + FL_UNSET_EMBED(ary); + ARY_SET_PTR(ary, ptr); + ARY_SET_CAPA(ary, capa); + ARY_SET_HEAP_LEN(ary, 0); + } + + return ary; +} + +VALUE +rb_ec_ary_new_from_values(rb_execution_context_t *ec, long n, const VALUE *elts) +{ + VALUE ary; + + ary = ec_ary_new(ec, rb_cArray, n); + if (n > 0 && elts) { + ary_memcpy(ary, 0, n, elts); + ARY_SET_LEN(ary, n); + } + + return ary; +} + VALUE rb_ary_tmp_new(long capa) { @@ -854,25 +933,26 @@ ary_make_shared(VALUE ary) long capa = ARY_CAPA(ary), len = RARRAY_LEN(ary); const VALUE *ptr; NEWOBJ_OF(shared, struct RArray, 0, T_ARRAY | (RGENGC_WB_PROTECTED_ARRAY ? FL_WB_PROTECTED : 0)); + VALUE vshared = (VALUE)shared; rb_ary_transient_heap_evacuate(ary, TRUE); ptr = ARY_HEAP_PTR(ary); - FL_UNSET_EMBED(shared); - ARY_SET_LEN((VALUE)shared, capa); - ARY_SET_PTR((VALUE)shared, ptr); - ary_mem_clear((VALUE)shared, len, capa - len); - FL_SET_SHARED_ROOT(shared); - ARY_SET_SHARED_ROOT_REFCNT((VALUE)shared, 1); + FL_UNSET_EMBED(vshared); + ARY_SET_LEN(vshared, capa); + ARY_SET_PTR(vshared, ptr); + ary_mem_clear(vshared, len, capa - len); + FL_SET_SHARED_ROOT(vshared); + ARY_SET_SHARED_ROOT_REFCNT(vshared, 1); FL_SET_SHARED(ary); RB_DEBUG_COUNTER_INC(obj_ary_shared_create); - ARY_SET_SHARED(ary, (VALUE)shared); - OBJ_FREEZE(shared); + ARY_SET_SHARED(ary, vshared); + OBJ_FREEZE(vshared); - ary_verify((VALUE)shared); + ary_verify(vshared); ary_verify(ary); - return (VALUE)shared; + return vshared; } } @@ -917,23 +997,24 @@ rb_check_to_array(VALUE ary) return rb_check_convert_type_with_id(ary, T_ARRAY, "Array", idTo_a); } +VALUE +rb_to_array(VALUE ary) +{ + return rb_convert_type_with_id(ary, T_ARRAY, "Array", idTo_a); +} + /* * call-seq: - * Array.try_convert(obj) -> array or nil + * Array.try_convert(object) -> object, new_array, or nil * - * Tries to convert +obj+ into an array, using the +to_ary+ method. Returns - * the converted array or +nil+ if +obj+ cannot be converted. - * This method can be used to check if an argument is an array. + * If +object+ is an \Array object, returns +object+. * - * Array.try_convert([1]) #=> [1] - * Array.try_convert("1") #=> nil + * Otherwise if +object+ responds to <tt>:to_ary</tt>, + * calls <tt>object.to_ary</tt> and returns the result. * - * if tmp = Array.try_convert(arg) - * # the argument is an array - * elsif tmp = String.try_convert(arg) - * # the argument is a string - * end + * Returns +nil+ if +object+ does not respond to <tt>:to_ary</tt> * + * Raises an exception unless <tt>object.to_ary</tt> returns an \Array object. */ static VALUE @@ -944,58 +1025,46 @@ rb_ary_s_try_convert(VALUE dummy, VALUE ary) /* * call-seq: - * Array.new(size=0, default=nil) - * Array.new(array) - * Array.new(size) {|index| block } - * - * Returns a new array. - * - * In the first form, if no arguments are sent, the new array will be empty. - * When a +size+ and an optional +default+ are sent, an array is created with - * +size+ copies of +default+. Take notice that all elements will reference the - * same object +default+. - * - * The second form creates a copy of the array passed as a parameter (the - * array is generated by calling to_ary on the parameter). - * - * first_array = ["Matz", "Guido"] - * - * second_array = Array.new(first_array) #=> ["Matz", "Guido"] - * - * first_array.equal? second_array #=> false - * - * In the last form, an array of the given size is created. Each element in - * this array is created by passing the element's index to the given block - * and storing the return value. - * - * Array.new(3) {|index| index ** 2} - * # => [0, 1, 4] - * - * == Common gotchas - * - * When sending the second parameter, the same object will be used as the - * value for all the array elements: - * - * a = Array.new(2, Hash.new) - * # => [{}, {}] - * - * a[0]['cat'] = 'feline' - * a # => [{"cat"=>"feline"}, {"cat"=>"feline"}] - * - * a[1]['cat'] = 'Felix' - * a # => [{"cat"=>"Felix"}, {"cat"=>"Felix"}] - * - * Since all the Array elements store the same hash, changes to one of them - * will affect them all. - * - * If multiple copies are what you want, you should use the block - * version which uses the result of that block each time an element - * of the array needs to be initialized: - * - * a = Array.new(2) {Hash.new} - * a[0]['cat'] = 'feline' - * a # => [{"cat"=>"feline"}, {}] - * + * Array.new -> new_empty_array + * Array.new(array) -> new_array + * Array.new(size) -> new_array + * Array.new(size, default_value) -> new_array + * Array.new(size) {|index| ... } -> new_array + * + * Returns a new \Array. + * + * With no block and no arguments, returns a new empty \Array object. + * + * With no block and a single \Array argument +array+, + * returns a new \Array formed from +array+: + * a = Array.new([:foo, 'bar', 2]) + * a.class # => Array + * a # => [:foo, "bar", 2] + * + * With no block and a single \Integer argument +size+, + * returns a new \Array of the given size + * whose elements are all +nil+: + * a = Array.new(3) + * a # => [nil, nil, nil] + * + * With no block and arguments +size+ and +default_value+, + * returns an \Array of the given size; + * each element is that same +default_value+: + * a = Array.new(3, 'x') + * a # => ['x', 'x', 'x'] + * + * With a block and argument +size+, + * returns an \Array of the given size; + * the block is called with each successive integer +index+; + * the element for that +index+ is the return value from the block: + * a = Array.new(3) {|index| "Element #{index}" } + * a # => ["Element 0", "Element 1", "Element 2"] + * + * Raises ArgumentError if +size+ is negative. + * + * With a block and no argument, + * or a single argument +0+, + * ignores the block and returns a new empty \Array. */ static VALUE @@ -1137,9 +1206,55 @@ ary_make_partial(VALUE ary, VALUE klass, long offset, long len) } static VALUE +ary_make_partial_step(VALUE ary, VALUE klass, long offset, long len, long step) +{ + assert(offset >= 0); + assert(len >= 0); + assert(offset+len <= RARRAY_LEN(ary)); + assert(step != 0); + + const VALUE *values = RARRAY_CONST_PTR_TRANSIENT(ary); + const long orig_len = len; + + if ((step > 0 && step >= len) || (step < 0 && (step < -len))) { + VALUE result = ary_new(klass, 1); + VALUE *ptr = (VALUE *)ARY_EMBED_PTR(result); + RB_OBJ_WRITE(result, ptr, values[offset]); + ARY_SET_EMBED_LEN(result, 1); + return result; + } + + long ustep = (step < 0) ? -step : step; + len = (len + ustep - 1) / ustep; + + long i; + long j = offset + ((step > 0) ? 0 : (orig_len - 1)); + VALUE result = ary_new(klass, len); + if (len <= RARRAY_EMBED_LEN_MAX) { + VALUE *ptr = (VALUE *)ARY_EMBED_PTR(result); + for (i = 0; i < len; ++i) { + RB_OBJ_WRITE(result, ptr+i, values[j]); + j += step; + } + ARY_SET_EMBED_LEN(result, len); + } + else { + RARRAY_PTR_USE_TRANSIENT(result, ptr, { + for (i = 0; i < len; ++i) { + RB_OBJ_WRITE(result, ptr+i, values[j]); + j += step; + } + }); + ARY_SET_LEN(result, len); + } + + return result; +} + +static VALUE ary_make_shared_copy(VALUE ary) { - return ary_make_partial(ary, rb_obj_class(ary), 0, RARRAY_LEN(ary)); + return ary_make_partial(ary, rb_cArray, 0, RARRAY_LEN(ary)); } enum ary_take_pos_flags @@ -1177,18 +1292,16 @@ ary_take_first_or_last(int argc, const VALUE *argv, VALUE ary, enum ary_take_pos /* * call-seq: - * ary << obj -> ary - * - * Append---Pushes the given object on to the end of this array. This - * expression returns the array itself, so several appends - * may be chained together. + * array << object -> self * - * a = [ 1, 2 ] - * a << "c" << "d" << [ 3, 4 ] - * #=> [ 1, 2, "c", "d", [ 3, 4 ] ] - * a - * #=> [ 1, 2, "c", "d", [ 3, 4 ] ] + * Appends +object+ to +self+; returns +self+: + * a = [:foo, 'bar', 2] + * a << :baz # => [:foo, "bar", 2, :baz] * + * Appends +object+ as one element, even if it is another \Array: + * a = [:foo, 'bar', 2] + * a1 = a << [3, 4] + * a1 # => [:foo, "bar", 2, [3, 4]] */ VALUE @@ -1216,19 +1329,22 @@ rb_ary_cat(VALUE ary, const VALUE *argv, long len) /* * call-seq: - * ary.push(obj, ...) -> ary - * ary.append(obj, ...) -> ary - * - * Append --- Pushes the given object(s) on to the end of this array. This - * expression returns the array itself, so several appends - * may be chained together. See also Array#pop for the opposite - * effect. - * - * a = [ "a", "b", "c" ] - * a.push("d", "e", "f") - * #=> ["a", "b", "c", "d", "e", "f"] - * [1, 2, 3].push(4).push(5) - * #=> [1, 2, 3, 4, 5] + * array.push(*objects) -> self + * + * Appends trailing elements. + * + * Appends each argument in +objects+ to +self+; returns +self+: + * a = [:foo, 'bar', 2] + * a.push(:baz, :bat) # => [:foo, "bar", 2, :baz, :bat] + * + * Appends each argument as one element, even if it is another \Array: + * a = [:foo, 'bar', 2] + * a1 = a.push([:baz, :bat], [:bam, :bad]) + * a1 # => [:foo, "bar", 2, [:baz, :bat], [:bam, :bad]] + * + * Array#append is an alias for \Array#push. + * + * Related: #pop, #shift, #unshift. */ static VALUE @@ -1258,20 +1374,30 @@ rb_ary_pop(VALUE ary) /* * call-seq: - * ary.pop -> obj or nil - * ary.pop(n) -> new_ary + * array.pop -> object or nil + * array.pop(n) -> new_array + * + * Removes and returns trailing elements. * - * Removes the last element from +self+ and returns it, or - * +nil+ if the array is empty. + * When no argument is given and +self+ is not empty, + * removes and returns the last element: + * a = [:foo, 'bar', 2] + * a.pop # => 2 + * a # => [:foo, "bar"] * - * If a number +n+ is given, returns an array of the last +n+ elements - * (or less) just like <code>array.slice!(-n, n)</code> does. See also - * Array#push for the opposite effect. + * Returns +nil+ if the array is empty. * - * a = [ "a", "b", "c", "d" ] - * a.pop #=> "d" - * a.pop(2) #=> ["b", "c"] - * a #=> ["a"] + * When a non-negative \Integer argument +n+ is given and is in range, + * removes and returns the last +n+ elements in a new \Array: + * a = [:foo, 'bar', 2] + * a.pop(2) # => ["bar", 2] + * + * If +n+ is positive and out of range, + * removes and returns all elements: + * a = [:foo, 'bar', 2] + * a.pop(50) # => [:foo, "bar", 2] + * + * Related: #push, #shift, #unshift. */ static VALUE @@ -1326,25 +1452,32 @@ rb_ary_shift(VALUE ary) /* * call-seq: - * ary.shift -> obj or nil - * ary.shift(n) -> new_ary + * array.shift -> object or nil + * array.shift(n) -> new_array + * + * Removes and returns leading elements. * - * Removes the first element of +self+ and returns it (shifting all - * other elements down by one). Returns +nil+ if the array - * is empty. + * When no argument is given, removes and returns the first element: + * a = [:foo, 'bar', 2] + * a.shift # => :foo + * a # => ['bar', 2] * - * If a number +n+ is given, returns an array of the first +n+ elements - * (or less) just like <code>array.slice!(0, n)</code> does. With +ary+ - * containing only the remainder elements, not including what was shifted to - * +new_ary+. See also Array#unshift for the opposite effect. + * Returns +nil+ if +self+ is empty. * - * args = [ "-m", "-q", "filename" ] - * args.shift #=> "-m" - * args #=> ["-q", "filename"] + * When positive \Integer argument +n+ is given, removes the first +n+ elements; + * returns those elements in a new \Array: + * a = [:foo, 'bar', 2] + * a.shift(2) # => [:foo, 'bar'] + * a # => [2] * - * args = [ "-m", "-q", "filename" ] - * args.shift(2) #=> ["-m", "-q"] - * args #=> ["filename"] + * If +n+ is as large as or larger than <tt>self.length</tt>, + * removes all elements; returns those elements in a new \Array: + * a = [:foo, 'bar', 2] + * a.shift(3) # => [:foo, 'bar', 2] + * + * If +n+ is zero, returns a new empty \Array; +self+ is unmodified. + * + * Related: #push, #pop, #unshift. */ static VALUE @@ -1365,59 +1498,75 @@ rb_ary_shift_m(int argc, VALUE *argv, VALUE ary) return result; } -MJIT_FUNC_EXPORTED VALUE -rb_ary_behead(VALUE ary, long n) +static VALUE +behead_shared(VALUE ary, long n) { - if (n<=0) return ary; + assert(ARY_SHARED_P(ary)); + rb_ary_modify_check(ary); + if (ARY_SHARED_ROOT_OCCUPIED(ARY_SHARED_ROOT(ary))) { + ary_mem_clear(ary, 0, n); + } + ARY_INCREASE_PTR(ary, n); + ARY_INCREASE_LEN(ary, -n); + ary_verify(ary); + return ary; +} +static VALUE +behead_transient(VALUE ary, long n) +{ rb_ary_modify_check(ary); - if (ARY_SHARED_P(ary)) { - if (ARY_SHARED_ROOT_OCCUPIED(ARY_SHARED_ROOT(ary))) { - setup_occupied_shared: - ary_mem_clear(ary, 0, n); - } - ARY_INCREASE_PTR(ary, n); + RARRAY_PTR_USE_TRANSIENT(ary, ptr, { + MEMMOVE(ptr, ptr+n, VALUE, RARRAY_LEN(ary)-n); + }); /* WB: no new reference */ + ARY_INCREASE_LEN(ary, -n); + ary_verify(ary); + return ary; +} + +MJIT_FUNC_EXPORTED VALUE +rb_ary_behead(VALUE ary, long n) +{ + if (n <= 0) { + return ary; + } + else if (ARY_SHARED_P(ary)) { + return behead_shared(ary, n); + } + else if (RARRAY_LEN(ary) >= ARY_DEFAULT_SIZE) { + ary_make_shared(ary); + return behead_shared(ary, n); } else { - if (RARRAY_LEN(ary) < ARY_DEFAULT_SIZE) { - RARRAY_PTR_USE_TRANSIENT(ary, ptr, { - MEMMOVE(ptr, ptr+n, VALUE, RARRAY_LEN(ary)-n); - }); /* WB: no new reference */ - } - else { - ary_make_shared(ary); - goto setup_occupied_shared; - } + return behead_transient(ary, n); } - ARY_INCREASE_LEN(ary, -n); +} + +static VALUE +make_room_for_unshift(VALUE ary, const VALUE *head, VALUE *sharedp, int argc, long capa, long len) +{ + if (head - sharedp < argc) { + long room = capa - len - argc; + + room -= room >> 4; + MEMMOVE((VALUE *)sharedp + argc + room, head, VALUE, len); + head = sharedp + argc + room; + } + ARY_SET_PTR(ary, head - argc); + assert(ARY_SHARED_ROOT_OCCUPIED(ARY_SHARED_ROOT(ary))); ary_verify(ary); - return ary; + return ARY_SHARED_ROOT(ary); } static VALUE -ary_ensure_room_for_unshift(VALUE ary, int argc) +ary_modify_for_unshift(VALUE ary, int argc) { long len = RARRAY_LEN(ary); long new_len = len + argc; long capa; const VALUE *head, *sharedp; - if (len > ARY_MAX_SIZE - argc) { - rb_raise(rb_eIndexError, "index %ld too big", new_len); - } - - if (ARY_SHARED_P(ary)) { - VALUE shared_root = ARY_SHARED_ROOT(ary); - capa = RARRAY_LEN(shared_root); - if (ARY_SHARED_ROOT_OCCUPIED(shared_root) && capa > new_len) { - rb_ary_modify_check(ary); - head = RARRAY_CONST_PTR_TRANSIENT(ary); - sharedp = RARRAY_CONST_PTR_TRANSIENT(shared_root); - goto makeroom_if_need; - } - } - rb_ary_modify(ary); capa = ARY_CAPA(ary); if (capa - (capa >> 6) <= new_len) { @@ -1433,21 +1582,7 @@ ary_ensure_room_for_unshift(VALUE ary, int argc) ary_make_shared(ary); head = sharedp = RARRAY_CONST_PTR_TRANSIENT(ary); - goto makeroom; - makeroom_if_need: - if (head - sharedp < argc) { - long room; - makeroom: - room = capa - new_len; - room -= room >> 4; - MEMMOVE((VALUE *)sharedp + argc + room, head, VALUE, len); - head = sharedp + argc + room; - } - ARY_SET_PTR(ary, head - argc); - assert(ARY_SHARED_ROOT_OCCUPIED(ARY_SHARED_ROOT(ary))); - - ary_verify(ary); - return ARY_SHARED_ROOT(ary); + return make_room_for_unshift(ary, head, (void *)sharedp, argc, capa, len); } else { /* sliding items */ @@ -1460,17 +1595,49 @@ ary_ensure_room_for_unshift(VALUE ary, int argc) } } +static VALUE +ary_ensure_room_for_unshift(VALUE ary, int argc) +{ + long len = RARRAY_LEN(ary); + long new_len = len + argc; + + if (len > ARY_MAX_SIZE - argc) { + rb_raise(rb_eIndexError, "index %ld too big", new_len); + } + else if (! ARY_SHARED_P(ary)) { + return ary_modify_for_unshift(ary, argc); + } + else { + VALUE shared_root = ARY_SHARED_ROOT(ary); + long capa = RARRAY_LEN(shared_root); + + if (! ARY_SHARED_ROOT_OCCUPIED(shared_root)) { + return ary_modify_for_unshift(ary, argc); + } + else if (new_len > capa) { + return ary_modify_for_unshift(ary, argc); + } + else { + const VALUE * head = RARRAY_CONST_PTR_TRANSIENT(ary); + void *sharedp = (void *)RARRAY_CONST_PTR_TRANSIENT(shared_root); + + rb_ary_modify_check(ary); + return make_room_for_unshift(ary, head, sharedp, argc, capa, len); + } + } +} + /* * call-seq: - * ary.unshift(obj, ...) -> ary - * ary.prepend(obj, ...) -> ary + * array.unshift(*objects) -> self + * + * Prepends the given +objects+ to +self+: + * a = [:foo, 'bar', 2] + * a.unshift(:bam, :bat) # => [:bam, :bat, :foo, "bar", 2] * - * Prepends objects to the front of +self+, moving other elements upwards. - * See also Array#shift for the opposite effect. + * Array#prepend is an alias for Array#unshift. * - * a = [ "b", "c", "d" ] - * a.unshift("a") #=> ["a", "b", "c", "d"] - * a.unshift(1, 2) #=> [ 1, 2, "a", "b", "c", "d"] + * Related: #push, #pop, #shift. */ static VALUE @@ -1515,7 +1682,7 @@ rb_ary_entry(VALUE ary, long offset) } VALUE -rb_ary_subseq(VALUE ary, long beg, long len) +rb_ary_subseq_step(VALUE ary, long beg, long len, long step) { VALUE klass; long alen = RARRAY_LEN(ary); @@ -1526,48 +1693,116 @@ rb_ary_subseq(VALUE ary, long beg, long len) if (alen < len || alen < beg + len) { len = alen - beg; } - klass = rb_obj_class(ary); + klass = rb_cArray; if (len == 0) return ary_new(klass, 0); + if (step == 0) + rb_raise(rb_eArgError, "slice step cannot be zero"); + if (step == 1) + return ary_make_partial(ary, klass, beg, len); + else + return ary_make_partial_step(ary, klass, beg, len, step); +} - return ary_make_partial(ary, klass, beg, len); +VALUE +rb_ary_subseq(VALUE ary, long beg, long len) +{ + return rb_ary_subseq_step(ary, beg, len, 1); } static VALUE rb_ary_aref2(VALUE ary, VALUE b, VALUE e); /* * call-seq: - * ary[index] -> obj or nil - * ary[start, length] -> new_ary or nil - * ary[range] -> new_ary or nil - * ary.slice(index) -> obj or nil - * ary.slice(start, length) -> new_ary or nil - * ary.slice(range) -> new_ary or nil - * - * Element Reference --- Returns the element at +index+, or returns a - * subarray starting at the +start+ index and continuing for +length+ - * elements, or returns a subarray specified by +range+ of indices. - * - * Negative indices count backward from the end of the array (-1 is the last - * element). For +start+ and +range+ cases the starting index is just before - * an element. Additionally, an empty array is returned when the starting - * index for an element range is at the end of the array. - * - * Returns +nil+ if the index (or starting index) are out of range. - * - * a = [ "a", "b", "c", "d", "e" ] - * a[2] + a[0] + a[1] #=> "cab" - * a[6] #=> nil - * a[1, 2] #=> [ "b", "c" ] - * a[1..3] #=> [ "b", "c", "d" ] - * a[4..7] #=> [ "e" ] - * a[6..10] #=> nil - * a[-3, 3] #=> [ "c", "d", "e" ] - * # special cases - * a[5] #=> nil - * a[6, 1] #=> nil - * a[5, 1] #=> [] - * a[5..10] #=> [] - * + * array[index] -> object or nil + * array[start, length] -> object or nil + * array[range] -> object or nil + * array[aseq] -> object or nil + * array.slice(index) -> object or nil + * array.slice(start, length) -> object or nil + * array.slice(range) -> object or nil + * array.slice(aseq) -> object or nil + * + * Returns elements from +self+; does not modify +self+. + * + * When a single \Integer argument +index+ is given, returns the element at offset +index+: + * a = [:foo, 'bar', 2] + * a[0] # => :foo + * a[2] # => 2 + * a # => [:foo, "bar", 2] + * + * If +index+ is negative, counts relative to the end of +self+: + * a = [:foo, 'bar', 2] + * a[-1] # => 2 + * a[-2] # => "bar" + * + * If +index+ is out of range, returns +nil+. + * + * When two \Integer arguments +start+ and +length+ are given, + * returns a new \Array of size +length+ containing successive elements beginning at offset +start+: + * a = [:foo, 'bar', 2] + * a[0, 2] # => [:foo, "bar"] + * a[1, 2] # => ["bar", 2] + * + * If <tt>start + length</tt> is greater than <tt>self.length</tt>, + * returns all elements from offset +start+ to the end: + * a = [:foo, 'bar', 2] + * a[0, 4] # => [:foo, "bar", 2] + * a[1, 3] # => ["bar", 2] + * a[2, 2] # => [2] + * + * If <tt>start == self.size</tt> and <tt>length >= 0</tt>, + * returns a new empty \Array. + * + * If +length+ is negative, returns +nil+. + * + * When a single \Range argument +range+ is given, + * treats <tt>range.min</tt> as +start+ above + * and <tt>range.size</tt> as +length+ above: + * a = [:foo, 'bar', 2] + * a[0..1] # => [:foo, "bar"] + * a[1..2] # => ["bar", 2] + * + * Special case: If <tt>range.start == a.size</tt>, returns a new empty \Array. + * + * If <tt>range.end</tt> is negative, calculates the end index from the end: + * a = [:foo, 'bar', 2] + * a[0..-1] # => [:foo, "bar", 2] + * a[0..-2] # => [:foo, "bar"] + * a[0..-3] # => [:foo] + * + * If <tt>range.start</tt> is negative, calculates the start index from the end: + * a = [:foo, 'bar', 2] + * a[-1..2] # => [2] + * a[-2..2] # => ["bar", 2] + * a[-3..2] # => [:foo, "bar", 2] + * + * If <tt>range.start</tt> is larger than the array size, returns +nil+. + * a = [:foo, 'bar', 2] + * a[4..1] # => nil + * a[4..0] # => nil + * a[4..-1] # => nil + * + * When a single Enumerator::ArithmeticSequence argument +aseq+ is given, + * returns an Array of elements corresponding to the indexes produced by + * the sequence. + * a = ['--', 'data1', '--', 'data2', '--', 'data3'] + * a[(1..).step(2)] # => ["data1", "data2", "data3"] + * + * Unlike slicing with range, if the start or the end of the arithmetic sequence + * is larger than array size, throws RangeError. + * a = ['--', 'data1', '--', 'data2', '--', 'data3'] + * a[(1..11).step(2)] + * # RangeError (((1..11).step(2)) out of range) + * a[(7..).step(2)] + * # RangeError (((7..).step(2)) out of range) + * + * If given a single argument, and its type is not one of the listed, tries to + * convert it to Integer, and raises if it is impossible: + * a = [:foo, 'bar', 2] + * # Raises TypeError (no implicit conversion of Symbol into Integer): + * a[:foo] + * + * Array#slice is an alias for Array#[]. */ VALUE @@ -1580,7 +1815,7 @@ rb_ary_aref(int argc, const VALUE *argv, VALUE ary) return rb_ary_aref1(ary, argv[0]); } -VALUE +static VALUE rb_ary_aref2(VALUE ary, VALUE b, VALUE e) { long beg = NUM2LONG(b); @@ -1594,35 +1829,33 @@ rb_ary_aref2(VALUE ary, VALUE b, VALUE e) MJIT_FUNC_EXPORTED VALUE rb_ary_aref1(VALUE ary, VALUE arg) { - long beg, len; + long beg, len, step; /* special case - speeding up */ if (FIXNUM_P(arg)) { return rb_ary_entry(ary, FIX2LONG(arg)); } - /* check if idx is Range */ - switch (rb_range_beg_len(arg, &beg, &len, RARRAY_LEN(ary), 0)) { + /* check if idx is Range or ArithmeticSequence */ + switch (rb_arithmetic_sequence_beg_len_step(arg, &beg, &len, &step, RARRAY_LEN(ary), 0)) { case Qfalse: - break; + break; case Qnil: - return Qnil; + return Qnil; default: - return rb_ary_subseq(ary, beg, len); + return rb_ary_subseq_step(ary, beg, len, step); } + return rb_ary_entry(ary, NUM2LONG(arg)); } /* * call-seq: - * ary.at(index) -> obj or nil - * - * Returns the element at +index+. A negative index counts from the end of - * +self+. Returns +nil+ if the index is out of range. See also - * Array#[]. + * array.at(index) -> object * - * a = [ "a", "b", "c", "d", "e" ] - * a.at(0) #=> "a" - * a.at(-1) #=> "e" + * Returns the element at \Integer offset +index+; does not modify +self+. + * a = [:foo, 'bar', 2] + * a.at(0) # => :foo + * a.at(2) # => 2 */ VALUE @@ -1633,19 +1866,33 @@ rb_ary_at(VALUE ary, VALUE pos) /* * call-seq: - * ary.first -> obj or nil - * ary.first(n) -> new_ary + * array.first -> object or nil + * array.first(n) -> new_array + * + * Returns elements from +self+; does not modify +self+. + * + * When no argument is given, returns the first element: + * a = [:foo, 'bar', 2] + * a.first # => :foo + * a # => [:foo, "bar", 2] + * + * If +self+ is empty, returns +nil+. * - * Returns the first element, or the first +n+ elements, of the array. - * If the array is empty, the first form returns +nil+, and the - * second form returns an empty array. See also Array#last for - * the opposite effect. + * When non-negative \Integer argument +n+ is given, + * returns the first +n+ elements in a new \Array: + * a = [:foo, 'bar', 2] + * a.first(2) # => [:foo, "bar"] * - * a = [ "q", "r", "s", "t" ] - * a.first #=> "q" - * a.first(2) #=> ["q", "r"] + * If <tt>n >= array.size</tt>, returns all elements: + * a = [:foo, 'bar', 2] + * a.first(50) # => [:foo, "bar", 2] + * + * If <tt>n == 0</tt> returns an new empty \Array: + * a = [:foo, 'bar', 2] + * a.first(0) # [] + * + * Related: #last. */ - static VALUE rb_ary_first(int argc, VALUE *argv, VALUE ary) { @@ -1660,17 +1907,32 @@ rb_ary_first(int argc, VALUE *argv, VALUE ary) /* * call-seq: - * ary.last -> obj or nil - * ary.last(n) -> new_ary + * array.last -> object or nil + * array.last(n) -> new_array + * + * Returns elements from +self+; +self+ is not modified. + * + * When no argument is given, returns the last element: + * a = [:foo, 'bar', 2] + * a.last # => 2 + * a # => [:foo, "bar", 2] + * + * If +self+ is empty, returns +nil+. * - * Returns the last element(s) of +self+. If the array is empty, - * the first form returns +nil+. + * When non-negative \Innteger argument +n+ is given, + * returns the last +n+ elements in a new \Array: + * a = [:foo, 'bar', 2] + * a.last(2) # => ["bar", 2] * - * See also Array#first for the opposite effect. + * If <tt>n >= array.size</tt>, returns all elements: + * a = [:foo, 'bar', 2] + * a.last(50) # => [:foo, "bar", 2] * - * a = [ "w", "x", "y", "z" ] - * a.last #=> "z" - * a.last(2) #=> ["y", "z"] + * If <tt>n == 0</tt>, returns an new empty \Array: + * a = [:foo, 'bar', 2] + * a.last(0) # [] + * + * Related: #first. */ VALUE @@ -1688,26 +1950,35 @@ rb_ary_last(int argc, const VALUE *argv, VALUE ary) /* * call-seq: - * ary.fetch(index) -> obj - * ary.fetch(index, default) -> obj - * ary.fetch(index) {|index| block} -> obj - * - * Tries to return the element at position +index+, but throws an IndexError - * exception if the referenced +index+ lies outside of the array bounds. This - * error can be prevented by supplying a second argument, which will act as a - * +default+ value. - * - * Alternatively, if a block is given it will only be executed when an - * invalid +index+ is referenced. - * - * Negative values of +index+ count from the end of the array. - * - * a = [ 11, 22, 33, 44 ] - * a.fetch(1) #=> 22 - * a.fetch(-1) #=> 44 - * a.fetch(4, 'cat') #=> "cat" - * a.fetch(100) {|i| puts "#{i} is out of bounds"} - * #=> "100 is out of bounds" + * array.fetch(index) -> element + * array.fetch(index, default_value) -> element + * array.fetch(index) {|index| ... } -> element + * + * Returns the element at offset +index+. + * + * With the single \Integer argument +index+, + * returns the element at offset +index+: + * a = [:foo, 'bar', 2] + * a.fetch(1) # => "bar" + * + * If +index+ is negative, counts from the end of the array: + * a = [:foo, 'bar', 2] + * a.fetch(-1) # => 2 + * a.fetch(-2) # => "bar" + * + * With arguments +index+ and +default_value+, + * returns the element at offset +index+ if index is in range, + * otherwise returns +default_value+: + * a = [:foo, 'bar', 2] + * a.fetch(1, nil) # => "bar" + * + * With argument +index+ and a block, + * returns the element at offset +index+ if index is in range + * (and the block is not called); otherwise calls the block with index and returns its return value: + * + * a = [:foo, 'bar', 2] + * a.fetch(1) {|index| raise 'Cannot happen' } # => "bar" + * a.fetch(50) {|index| "Value for #{index}" } # => "Value for 50" */ static VALUE @@ -1740,28 +2011,37 @@ rb_ary_fetch(int argc, VALUE *argv, VALUE ary) /* * call-seq: - * ary.find_index(obj) -> int or nil - * ary.find_index {|item| block} -> int or nil - * ary.find_index -> Enumerator - * ary.index(obj) -> int or nil - * ary.index {|item| block} -> int or nil - * ary.index -> Enumerator + * array.index(object) -> integer or nil + * array.index {|element| ... } -> integer or nil + * array.index -> new_enumerator + * + * Returns the index of a specified element. + * + * When argument +object+ is given but no block, + * returns the index of the first element +element+ + * for which <tt>object == element</tt>: + * a = [:foo, 'bar', 2, 'bar'] + * a.index('bar') # => 1 * - * Returns the _index_ of the first object in +ary+ such that the object is - * <code>==</code> to +obj+. + * Returns +nil+ if no such element found. * - * If a block is given instead of an argument, returns the _index_ of the - * first object for which the block returns +true+. Returns +nil+ if no - * match is found. + * When both argument +object+ and a block are given, + * calls the block with each successive element; + * returns the index of the first element for which the block returns a truthy value: + * a = [:foo, 'bar', 2, 'bar'] + * a.index {|element| element == 'bar' } # => 1 * - * See also Array#rindex. + * Returns +nil+ if the block never returns a truthy value. * - * An Enumerator is returned if neither a block nor argument is given. + * When neither an argument nor a block is given, returns a new Enumerator: + * a = [:foo, 'bar', 2] + * e = a.index + * e # => #<Enumerator: [:foo, "bar", 2]:index> + * e.each {|element| element == 'bar' } # => 1 * - * a = [ "a", "b", "c" ] - * a.index("b") #=> 1 - * a.index("z") #=> nil - * a.index {|x| x == "b"} #=> 1 + * Array#find_index is an alias for Array#index. + * + * Related: #rindex. */ static VALUE @@ -1794,26 +2074,33 @@ rb_ary_index(int argc, VALUE *argv, VALUE ary) /* * call-seq: - * ary.rindex(obj) -> int or nil - * ary.rindex {|item| block} -> int or nil - * ary.rindex -> Enumerator + * array.rindex(object) -> integer or nil + * array.rindex {|element| ... } -> integer or nil + * array.rindex -> new_enumerator + * + * Returns the index of the last element for which <tt>object == element</tt>. + * + * When argument +object+ is given but no block, returns the index of the last such element found: + * a = [:foo, 'bar', 2, 'bar'] + * a.rindex('bar') # => 3 * - * Returns the _index_ of the last object in +self+ <code>==</code> to +obj+. + * Returns +nil+ if no such object found. * - * If a block is given instead of an argument, returns the _index_ of the - * first object for which the block returns +true+, starting from the last - * object. + * When a block is given but no argument, calls the block with each successive element; + * returns the index of the last element for which the block returns a truthy value: + * a = [:foo, 'bar', 2, 'bar'] + * a.rindex {|element| element == 'bar' } # => 3 * - * Returns +nil+ if no match is found. + * Returns +nil+ if the block never returns a truthy value. * - * See also Array#index. + * When neither an argument nor a block is given, returns a new \Enumerator: * - * If neither block nor argument is given, an Enumerator is returned instead. + * a = [:foo, 'bar', 2, 'bar'] + * e = a.rindex + * e # => #<Enumerator: [:foo, "bar", 2, "bar"]:rindex> + * e.each {|element| element == 'bar' } # => 3 * - * a = [ "a", "b", "b", "b", "c" ] - * a.rindex("b") #=> 3 - * a.rindex("z") #=> nil - * a.rindex {|x| x == "b"} #=> 3 + * Related: #index. */ static VALUE @@ -1943,14 +2230,6 @@ rb_ary_set_len(VALUE ary, long len) ARY_SET_LEN(ary, len); } -/*! - * expands or shrinks \a ary to \a len elements. - * expanded region will be filled with Qnil. - * \param ary an array - * \param len new size - * \return \a ary - * \post the size of \a ary is \a len. - */ VALUE rb_ary_resize(VALUE ary, long len) { @@ -1981,8 +2260,8 @@ rb_ary_resize(VALUE ary, long len) } else { if (olen > len + ARY_DEFAULT_SIZE) { - ary_heap_realloc(ary, len); - ARY_SET_CAPA(ary, len); + size_t new_capa = ary_heap_realloc(ary, len); + ARY_SET_CAPA(ary, new_capa); } ARY_SET_HEAP_LEN(ary, len); } @@ -1990,88 +2269,175 @@ rb_ary_resize(VALUE ary, long len) return ary; } +static VALUE +ary_aset_by_rb_ary_store(VALUE ary, long key, VALUE val) +{ + rb_ary_store(ary, key, val); + return val; +} + +static VALUE +ary_aset_by_rb_ary_splice(VALUE ary, long beg, long len, VALUE val) +{ + VALUE rpl = rb_ary_to_ary(val); + rb_ary_splice(ary, beg, len, RARRAY_CONST_PTR_TRANSIENT(rpl), RARRAY_LEN(rpl)); + RB_GC_GUARD(rpl); + return val; +} + /* * call-seq: - * ary[index] = obj -> obj - * ary[start, length] = obj or other_ary or nil -> obj or other_ary or nil - * ary[range] = obj or other_ary or nil -> obj or other_ary or nil - * - * Element Assignment --- Sets the element at +index+, or replaces a subarray - * from the +start+ index for +length+ elements, or replaces a subarray - * specified by the +range+ of indices. - * - * If indices are greater than the current capacity of the array, the array - * grows automatically. Elements are inserted into the array at +start+ if - * +length+ is zero. - * - * Negative indices will count backward from the end of the array. For - * +start+ and +range+ cases the starting index is just before an element. - * - * An IndexError is raised if a negative index points past the beginning of - * the array. - * - * See also Array#push, and Array#unshift. - * - * a = Array.new - * a[4] = "4"; #=> [nil, nil, nil, nil, "4"] - * a[0, 3] = [ 'a', 'b', 'c' ] #=> ["a", "b", "c", nil, "4"] - * a[1..2] = [ 1, 2 ] #=> ["a", 1, 2, nil, "4"] - * a[0, 2] = "?" #=> ["?", 2, nil, "4"] - * a[0..2] = "A" #=> ["A", "4"] - * a[-1] = "Z" #=> ["A", "Z"] - * a[1..-1] = nil #=> ["A", nil] - * a[1..-1] = [] #=> ["A"] - * a[0, 0] = [ 1, 2 ] #=> [1, 2, "A"] - * a[3, 0] = "B" #=> [1, 2, "A", "B"] + * array[index] = object -> object + * array[start, length] = object -> object + * array[range] = object -> object + * + * Assigns elements in +self+; returns the given +object+. + * + * When \Integer argument +index+ is given, assigns +object+ to an element in +self+. + * + * If +index+ is non-negative, assigns +object+ the element at offset +index+: + * a = [:foo, 'bar', 2] + * a[0] = 'foo' # => "foo" + * a # => ["foo", "bar", 2] + * + * If +index+ is greater than <tt>self.length</tt>, extends the array: + * a = [:foo, 'bar', 2] + * a[7] = 'foo' # => "foo" + * a # => [:foo, "bar", 2, nil, nil, nil, nil, "foo"] + * + * If +index+ is negative, counts backwards from the end of the array: + * a = [:foo, 'bar', 2] + * a[-1] = 'two' # => "two" + * a # => [:foo, "bar", "two"] + * + * When \Integer arguments +start+ and +length+ are given and +object+ is not an \Array, + * removes <tt>length - 1</tt> elements beginning at offset +start+, + * and assigns +object+ at offset +start+: + * a = [:foo, 'bar', 2] + * a[0, 2] = 'foo' # => "foo" + * a # => ["foo", 2] + * + * If +start+ is negative, counts backwards from the end of the array: + * a = [:foo, 'bar', 2] + * a[-2, 2] = 'foo' # => "foo" + * a # => [:foo, "foo"] + * + * If +start+ is non-negative and outside the array (<tt> >= self.size</tt>), + * extends the array with +nil+, assigns +object+ at offset +start+, + * and ignores +length+: + * a = [:foo, 'bar', 2] + * a[6, 50] = 'foo' # => "foo" + * a # => [:foo, "bar", 2, nil, nil, nil, "foo"] + * + * If +length+ is zero, shifts elements at and following offset +start+ + * and assigns +object+ at offset +start+: + * a = [:foo, 'bar', 2] + * a[1, 0] = 'foo' # => "foo" + * a # => [:foo, "foo", "bar", 2] + * + * If +length+ is too large for the existing array, does not extend the array: + * a = [:foo, 'bar', 2] + * a[1, 5] = 'foo' # => "foo" + * a # => [:foo, "foo"] + * + * When \Range argument +range+ is given and +object+ is an \Array, + * removes <tt>length - 1</tt> elements beginning at offset +start+, + * and assigns +object+ at offset +start+: + * a = [:foo, 'bar', 2] + * a[0..1] = 'foo' # => "foo" + * a # => ["foo", 2] + * + * if <tt>range.begin</tt> is negative, counts backwards from the end of the array: + * a = [:foo, 'bar', 2] + * a[-2..2] = 'foo' # => "foo" + * a # => [:foo, "foo"] + * + * If the array length is less than <tt>range.begin</tt>, + * assigns +object+ at offset <tt>range.begin</tt>, and ignores +length+: + * a = [:foo, 'bar', 2] + * a[6..50] = 'foo' # => "foo" + * a # => [:foo, "bar", 2, nil, nil, nil, "foo"] + * + * If <tt>range.end</tt> is zero, shifts elements at and following offset +start+ + * and assigns +object+ at offset +start+: + * a = [:foo, 'bar', 2] + * a[1..0] = 'foo' # => "foo" + * a # => [:foo, "foo", "bar", 2] + * + * If <tt>range.end</tt> is negative, assigns +object+ at offset +start+, + * retains <tt>range.end.abs -1</tt> elements past that, and removes those beyond: + * a = [:foo, 'bar', 2] + * a[1..-1] = 'foo' # => "foo" + * a # => [:foo, "foo"] + * a = [:foo, 'bar', 2] + * a[1..-2] = 'foo' # => "foo" + * a # => [:foo, "foo", 2] + * a = [:foo, 'bar', 2] + * a[1..-3] = 'foo' # => "foo" + * a # => [:foo, "foo", "bar", 2] + * a = [:foo, 'bar', 2] + * + * If <tt>range.end</tt> is too large for the existing array, + * replaces array elements, but does not extend the array with +nil+ values: + * a = [:foo, 'bar', 2] + * a[1..5] = 'foo' # => "foo" + * a # => [:foo, "foo"] */ static VALUE rb_ary_aset(int argc, VALUE *argv, VALUE ary) { long offset, beg, len; - VALUE rpl; + rb_check_arity(argc, 2, 3); + rb_ary_modify_check(ary); if (argc == 3) { - rb_ary_modify_check(ary); beg = NUM2LONG(argv[0]); len = NUM2LONG(argv[1]); - goto range; + return ary_aset_by_rb_ary_splice(ary, beg, len, argv[2]); } - rb_check_arity(argc, 2, 2); - rb_ary_modify_check(ary); if (FIXNUM_P(argv[0])) { offset = FIX2LONG(argv[0]); - goto fixnum; + return ary_aset_by_rb_ary_store(ary, offset, argv[1]); } if (rb_range_beg_len(argv[0], &beg, &len, RARRAY_LEN(ary), 1)) { /* check if idx is Range */ - range: - rpl = rb_ary_to_ary(argv[argc-1]); - rb_ary_splice(ary, beg, len, RARRAY_CONST_PTR_TRANSIENT(rpl), RARRAY_LEN(rpl)); - RB_GC_GUARD(rpl); - return argv[argc-1]; + return ary_aset_by_rb_ary_splice(ary, beg, len, argv[1]); } offset = NUM2LONG(argv[0]); -fixnum: - rb_ary_store(ary, offset, argv[1]); - return argv[1]; + return ary_aset_by_rb_ary_store(ary, offset, argv[1]); } /* * call-seq: - * ary.insert(index, obj...) -> ary + * array.insert(index, *objects) -> self * - * Inserts the given values before the element with the given +index+. - * - * Negative indices count backwards from the end of the array, where +-1+ is - * the last element. If a negative index is used, the given values will be - * inserted after that element, so using an index of +-1+ will insert the - * values at the end of the array. + * Inserts given +objects+ before or after the element at \Integer index +offset+; + * returns +self+. * - * a = %w{ a b c d } - * a.insert(2, 99) #=> ["a", "b", 99, "c", "d"] - * a.insert(-2, 1, 2, 3) #=> ["a", "b", 99, "c", 1, 2, 3, "d"] + * When +index+ is non-negative, inserts all given +objects+ + * before the element at offset +index+: + * a = [:foo, 'bar', 2] + * a.insert(1, :bat, :bam) # => [:foo, :bat, :bam, "bar", 2] + * + * Extends the array if +index+ is beyond the array (<tt>index >= self.size</tt>): + * a = [:foo, 'bar', 2] + * a.insert(5, :bat, :bam) + * a # => [:foo, "bar", 2, nil, nil, :bat, :bam] + * + * Does nothing if no objects given: + * a = [:foo, 'bar', 2] + * a.insert(1) + * a.insert(50) + * a.insert(-50) + * a # => [:foo, "bar", 2] + * + * When +index+ is negative, inserts all given +objects+ + * _after_ the element at offset <tt>index+self.size</tt>: + * a = [:foo, 'bar', 2] + * a.insert(-2, :bat, :bam) + * a # => [:foo, "bar", :bat, :bam, 2] */ static VALUE @@ -2109,20 +2475,41 @@ ary_enum_length(VALUE ary, VALUE args, VALUE eobj) /* * call-seq: - * ary.each {|item| block} -> ary - * ary.each -> Enumerator + * array.each {|element| ... } -> self + * array.each -> Enumerator + * + * Iterates over array elements. * - * Calls the given block once for each element in +self+, passing that element - * as a parameter. Returns the array itself. + * When a block given, passes each successive array element to the block; + * returns +self+: + * a = [:foo, 'bar', 2] + * a.each {|element| puts "#{element.class} #{element}" } * - * If no block is given, an Enumerator is returned. + * Output: + * Symbol foo + * String bar + * Integer 2 * - * a = [ "a", "b", "c" ] - * a.each {|x| print x, " -- " } + * Allows the array to be modified during iteration: + * a = [:foo, 'bar', 2] + * a.each {|element| puts element; a.clear if element.to_s.start_with?('b') } * - * produces: + * Output: + * foo + * bar * - * a -- b -- c -- + * When no block given, returns a new \Enumerator: + * a = [:foo, 'bar', 2] + * e = a.each + * e # => #<Enumerator: [:foo, "bar", 2]:each> + * a1 = e.each {|element| puts "#{element.class} #{element}" } + * + * Output: + * Symbol foo + * String bar + * Integer 2 + * + * Related: #each_index, #reverse_each. */ VALUE @@ -2139,20 +2526,41 @@ rb_ary_each(VALUE ary) /* * call-seq: - * ary.each_index {|index| block} -> ary - * ary.each_index -> Enumerator + * array.each_index {|index| ... } -> self + * array.each_index -> Enumerator + * + * Iterates over array indexes. * - * Same as Array#each, but passes the +index+ of the element instead of the - * element itself. + * When a block given, passes each successive array index to the block; + * returns +self+: + * a = [:foo, 'bar', 2] + * a.each_index {|index| puts "#{index} #{a[index]}" } * - * An Enumerator is returned if no block is given. + * Output: + * 0 foo + * 1 bar + * 2 2 * - * a = [ "a", "b", "c" ] - * a.each_index {|x| print x, " -- " } + * Allows the array to be modified during iteration: + * a = [:foo, 'bar', 2] + * a.each_index {|index| puts index; a.clear if index > 0 } * - * produces: + * Output: + * 0 + * 1 * - * 0 -- 1 -- 2 -- + * When no block given, returns a new \Enumerator: + * a = [:foo, 'bar', 2] + * e = a.each_index + * e # => #<Enumerator: [:foo, "bar", 2]:each_index> + * a1 = e.each {|index| puts "#{index} #{a[index]}"} + * + * Output: + * 0 foo + * 1 bar + * 2 2 + * + * Related: #each, #reverse_each. */ static VALUE @@ -2169,17 +2577,40 @@ rb_ary_each_index(VALUE ary) /* * call-seq: - * ary.reverse_each {|item| block} -> ary - * ary.reverse_each -> Enumerator - * - * Same as Array#each, but traverses +self+ in reverse order. - * - * a = [ "a", "b", "c" ] - * a.reverse_each {|x| print x, " " } - * - * produces: - * - * c b a + * array.reverse_each {|element| ... } -> self + * array.reverse_each -> Enumerator + * + * Iterates backwards over array elements. + * + * When a block given, passes, in reverse order, each element to the block; + * returns +self+: + * a = [:foo, 'bar', 2] + * a.reverse_each {|element| puts "#{element.class} #{element}" } + * + * Output: + * Integer 2 + * String bar + * Symbol foo + * + * Allows the array to be modified during iteration: + * a = [:foo, 'bar', 2] + * a.reverse_each {|element| puts element; a.clear if element.to_s.start_with?('b') } + * + * Output: + * 2 + * bar + * + * When no block given, returns a new \Enumerator: + * a = [:foo, 'bar', 2] + * e = a.reverse_each + * e # => #<Enumerator: [:foo, "bar", 2]:reverse_each> + * a1 = e.each {|element| puts "#{element.class} #{element}" } + * Output: + * Integer 2 + * String bar + * Symbol foo + * + * Related: #each, #each_index. */ static VALUE @@ -2202,12 +2633,9 @@ rb_ary_reverse_each(VALUE ary) /* * call-seq: - * ary.length -> int + * array.length -> an_integer * - * Returns the number of elements in +self+. May be zero. - * - * [ 1, 2, 3, 4, 5 ].length #=> 5 - * [].length #=> 0 + * Returns the count of elements in +self+. */ static VALUE @@ -2219,19 +2647,16 @@ rb_ary_length(VALUE ary) /* * call-seq: - * ary.empty? -> true or false - * - * Returns +true+ if +self+ contains no elements. + * array.empty? -> true or false * - * [].empty? #=> true + * Returns +true+ if the count of elements in +self+ is zero, + * +false+ otherwise. */ static VALUE rb_ary_empty_p(VALUE ary) { - if (RARRAY_LEN(ary) == 0) - return Qtrue; - return Qfalse; + return RBOOL(RARRAY_LEN(ary) == 0); } VALUE @@ -2275,7 +2700,7 @@ recursive_join(VALUE obj, VALUE argp, int recur) return Qnil; } -static void +static long ary_join_0(VALUE ary, VALUE sep, long max, VALUE result) { long i; @@ -2284,10 +2709,40 @@ ary_join_0(VALUE ary, VALUE sep, long max, VALUE result) if (max > 0) rb_enc_copy(result, RARRAY_AREF(ary, 0)); for (i=0; i<max; i++) { val = RARRAY_AREF(ary, i); + if (!RB_TYPE_P(val, T_STRING)) break; if (i > 0 && !NIL_P(sep)) rb_str_buf_append(result, sep); rb_str_buf_append(result, val); } + return i; +} + +static void +ary_join_1_str(VALUE dst, VALUE src, int *first) +{ + rb_str_buf_append(dst, src); + if (*first) { + rb_enc_copy(dst, src); + *first = FALSE; + } +} + +static void +ary_join_1_ary(VALUE obj, VALUE ary, VALUE sep, VALUE result, VALUE val, int *first) +{ + if (val == ary) { + rb_raise(rb_eArgError, "recursive array join"); + } + else { + VALUE args[4]; + + *first = FALSE; + args[0] = val; + args[1] = sep; + args[2] = result; + args[3] = (VALUE)first; + rb_exec_recursive(recursive_join, obj, (VALUE)args); + } } static void @@ -2301,44 +2756,19 @@ ary_join_1(VALUE obj, VALUE ary, VALUE sep, long i, VALUE result, int *first) val = RARRAY_AREF(ary, i); if (RB_TYPE_P(val, T_STRING)) { - str_join: - rb_str_buf_append(result, val); - if (*first) { - rb_enc_copy(result, val); - *first = FALSE; - } + ary_join_1_str(result, val, first); } else if (RB_TYPE_P(val, T_ARRAY)) { - obj = val; - ary_join: - if (val == ary) { - rb_raise(rb_eArgError, "recursive array join"); - } - else { - VALUE args[4]; - - *first = FALSE; - args[0] = val; - args[1] = sep; - args[2] = result; - args[3] = (VALUE)first; - rb_exec_recursive(recursive_join, obj, (VALUE)args); - } + ary_join_1_ary(val, ary, sep, result, val, first); } - else { - tmp = rb_check_string_type(val); - if (!NIL_P(tmp)) { - val = tmp; - goto str_join; - } - tmp = rb_check_array_type(val); - if (!NIL_P(tmp)) { - obj = val; - val = tmp; - goto ary_join; - } - val = rb_obj_as_string(val); - goto str_join; + else if (!NIL_P(tmp = rb_check_string_type(val))) { + ary_join_1_str(result, tmp, first); + } + else if (!NIL_P(tmp = rb_check_array_type(val))) { + ary_join_1_ary(val, ary, sep, result, tmp, first); + } + else { + ary_join_1_str(result, rb_obj_as_string(val), first); } } } @@ -2361,9 +2791,11 @@ rb_ary_join(VALUE ary, VALUE sep) if (NIL_P(tmp) || tmp != val) { int first; - result = rb_str_buf_new(len + (RARRAY_LEN(ary)-i)*10); + long n = RARRAY_LEN(ary); + if (i > n) i = n; + result = rb_str_buf_new(len + (n-i)*10); rb_enc_associate(result, rb_usascii_encoding()); - ary_join_0(ary, sep, i, result); + i = ary_join_0(ary, sep, i, result); first = i == 0; ary_join_1(ary, ary, sep, i, result, &first); return result; @@ -2382,22 +2814,27 @@ rb_ary_join(VALUE ary, VALUE sep) /* * call-seq: - * ary.join(separator=$,) -> str - * - * Returns a string created by converting each element of the array to - * a string, separated by the given +separator+. - * If the +separator+ is +nil+, it uses current <code>$,</code>. - * If both the +separator+ and <code>$,</code> are +nil+, - * it uses an empty string. - * - * [ "a", "b", "c" ].join #=> "abc" - * [ "a", "b", "c" ].join("-") #=> "a-b-c" - * - * For nested arrays, join is applied recursively: - * - * [ "a", [1, 2, [:x, :y]], "b" ].join("-") #=> "a-1-2-x-y-b" + * array.join ->new_string + * array.join(separator = $,) -> new_string + * + * Returns the new \String formed by joining the array elements after conversion. + * For each element +element+ + * - Uses <tt>element.to_s</tt> if +element+ is not a <tt>kind_of?(Array)</tt>. + * - Uses recursive <tt>element.join(separator)</tt> if +element+ is a <tt>kind_of?(Array)</tt>. + * + * With no argument, joins using the output field separator, <tt>$,</tt>: + * a = [:foo, 'bar', 2] + * $, # => nil + * a.join # => "foobar2" + * + * With \string argument +separator+, joins using that separator: + * a = [:foo, 'bar', 2] + * a.join("\n") # => "foo\nbar\n2" + * + * Joins recursively for nested Arrays: + * a = [:foo, [:bar, [:baz, :bat]]] + * a.join # => "foobarbazbat" */ - static VALUE rb_ary_join_m(int argc, VALUE *argv, VALUE ary) { @@ -2406,7 +2843,7 @@ rb_ary_join_m(int argc, VALUE *argv, VALUE ary) if (rb_check_arity(argc, 0, 1) == 0 || NIL_P(sep = argv[0])) { sep = rb_output_fs; if (!NIL_P(sep)) { - rb_warn("$, is set to non-nil value"); + rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "$, is set to non-nil value"); } } @@ -2433,13 +2870,14 @@ inspect_ary(VALUE ary, VALUE dummy, int recur) /* * call-seq: - * ary.inspect -> string - * ary.to_s -> string + * array.inspect -> new_string * - * Creates a string representation of +self+, by calling #inspect - * on each element. + * Returns the new \String formed by calling method <tt>#inspect</tt> + * on each array element: + * a = [:foo, 'bar', 2] + * a.inspect # => "[:foo, \"bar\", 2]" * - * [ "a", "b", "c" ].to_s #=> "[\"a\", \"b\", \"c\"]" + * Array#to_s is an alias for Array#inspect. */ static VALUE @@ -2457,11 +2895,20 @@ rb_ary_to_s(VALUE ary) /* * call-seq: - * ary.to_a -> ary - * - * Returns +self+. - * - * If called on a subclass of Array, converts the receiver to an Array object. + * to_a -> self or new_array + * + * When +self+ is an instance of \Array, returns +self+: + * a = [:foo, 'bar', 2] + * a.to_a # => [:foo, "bar", 2] + * + * Otherwise, returns a new \Array containing the elements of +self+: + * class MyArray < Array; end + * a = MyArray.new(['foo', 'bar', 'two']) + * a.instance_of?(Array) # => false + * a.kind_of?(Array) # => true + * a1 = a.to_a + * a1 # => ["foo", "bar", "two"] + * a1.class # => Array # Not MyArray */ static VALUE @@ -2477,20 +2924,24 @@ rb_ary_to_a(VALUE ary) /* * call-seq: - * ary.to_h -> hash - * ary.to_h {|item| block } -> hash - * - * Returns the result of interpreting <i>ary</i> as an array of - * <tt>[key, value]</tt> pairs. - * - * [[:foo, :bar], [1, 2]].to_h - * # => {:foo => :bar, 1 => 2} - * - * If a block is given, the results of the block on each element of - * the array will be used as pairs. - * - * ["foo", "bar"].to_h {|s| [s.ord, s]} - * # => {102=>"foo", 98=>"bar"} + * array.to_h -> new_hash + * array.to_h {|item| ... } -> new_hash + * + * Returns a new \Hash formed from +self+. + * + * When a block is given, calls the block with each array element; + * the block must return a 2-element \Array whose two elements + * form a key-value pair in the returned \Hash: + * a = ['foo', :bar, 1, [2, 3], {baz: 4}] + * h = a.to_h {|item| [item, item] } + * h # => {"foo"=>"foo", :bar=>:bar, 1=>1, [2, 3]=>[2, 3], {:baz=>4}=>{:baz=>4}} + * + * When no block is given, +self+ must be an \Array of 2-element sub-arrays, + * each sub-array is formed into a key-value pair in the new \Hash: + * [].to_h # => {} + * a = [['foo', 'zero'], ['bar', 'one'], ['baz', 'two']] + * h = a.to_h + * h # => {"foo"=>"zero", "bar"=>"one", "baz"=>"two"} */ static VALUE @@ -2519,7 +2970,7 @@ rb_ary_to_h(VALUE ary) /* * call-seq: - * ary.to_ary -> ary + * array.to_ary -> self * * Returns +self+. */ @@ -2558,13 +3009,11 @@ rb_ary_reverse(VALUE ary) /* * call-seq: - * ary.reverse! -> ary - * - * Reverses +self+ in place. + * array.reverse! -> self * - * a = [ "a", "b", "c" ] - * a.reverse! #=> ["c", "b", "a"] - * a #=> ["c", "b", "a"] + * Reverses +self+ in place: + * a = ['foo', 'bar', 'two'] + * a.reverse! # => ["two", "bar", "foo"] */ static VALUE @@ -2575,12 +3024,12 @@ rb_ary_reverse_bang(VALUE ary) /* * call-seq: - * ary.reverse -> new_ary + * array.reverse -> new_array * - * Returns a new array containing +self+'s elements in reverse order. - * - * [ "a", "b", "c" ].reverse #=> ["c", "b", "a"] - * [ 1 ].reverse #=> [1] + * Returns a new \Array with the elements of +self+ in reverse order. + * a = ['foo', 'bar', 'two'] + * a1 = a.reverse + * a1 # => ["two", "bar", "foo"] */ static VALUE @@ -2607,10 +3056,22 @@ rotate_count(long cnt, long len) static void ary_rotate_ptr(VALUE *ptr, long len, long cnt) { - --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); + if (cnt == 1) { + VALUE tmp = *ptr; + memmove(ptr, ptr + 1, sizeof(VALUE)*(len - 1)); + *(ptr + len - 1) = tmp; + } + else if (cnt == len - 1) { + VALUE tmp = *(ptr + len - 1); + memmove(ptr + 1, ptr, sizeof(VALUE)*(len - 1)); + *ptr = tmp; + } + else { + --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); + } } VALUE @@ -2620,7 +3081,7 @@ rb_ary_rotate(VALUE ary, long cnt) if (cnt != 0) { long len = RARRAY_LEN(ary); - if (len > 0 && (cnt = rotate_count(cnt, len)) > 0) { + if (len > 1 && (cnt = rotate_count(cnt, len)) > 0) { RARRAY_PTR_USE_TRANSIENT(ary, ptr, ary_rotate_ptr(ptr, len, cnt)); return ary; } @@ -2630,19 +3091,41 @@ rb_ary_rotate(VALUE ary, long cnt) /* * call-seq: - * ary.rotate!(count=1) -> ary - * - * Rotates +self+ in place so that the element at +count+ comes first, and - * returns +self+. - * - * If +count+ is negative then it rotates in the opposite direction, starting - * from the end of the array where +-1+ is the last element. - * - * 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"] + * array.rotate! -> self + * array.rotate!(count) -> self + * + * Rotates +self+ in place by moving elements from one end to the other; returns +self+. + * + * When no argument given, rotates the first element to the last position: + * a = [:foo, 'bar', 2, 'bar'] + * a.rotate! # => ["bar", 2, "bar", :foo] + * + * When given a non-negative \Integer +count+, + * rotates +count+ elements from the beginning to the end: + * a = [:foo, 'bar', 2] + * a.rotate!(2) + * a # => [2, :foo, "bar"] + * + * If +count+ is large, uses <tt>count % array.size</tt> as the count: + * a = [:foo, 'bar', 2] + * a.rotate!(20) + * a # => [2, :foo, "bar"] + * + * If +count+ is zero, returns +self+ unmodified: + * a = [:foo, 'bar', 2] + * a.rotate!(0) + * a # => [:foo, "bar", 2] + * + * When given a negative Integer +count+, rotates in the opposite direction, + * from end to beginning: + * a = [:foo, 'bar', 2] + * a.rotate!(-2) + * a # => ["bar", 2, :foo] + * + * If +count+ is small (far from zero), uses <tt>count % array.size</tt> as the count: + * a = [:foo, 'bar', 2] + * a.rotate!(-5) + * a # => ["bar", 2, :foo] */ static VALUE @@ -2655,19 +3138,44 @@ rb_ary_rotate_bang(int argc, VALUE *argv, VALUE ary) /* * call-seq: - * ary.rotate(count=1) -> new_ary - * - * Returns a new array by rotating +self+ so that the element at +count+ is - * the first element of the new array. - * - * If +count+ is negative then it rotates in the opposite direction, starting - * from the end of +self+ where +-1+ is the last element. - * - * 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"] + * array.rotate -> new_array + * array.rotate(count) -> new_array + * + * Returns a new \Array formed from +self+ with elements + * rotated from one end to the other. + * + * When no argument given, returns a new \Array that is like +self+, + * except that the first element has been rotated to the last position: + * a = [:foo, 'bar', 2, 'bar'] + * a1 = a.rotate + * a1 # => ["bar", 2, "bar", :foo] + * + * When given a non-negative \Integer +count+, + * returns a new \Array with +count+ elements rotated from the beginning to the end: + * a = [:foo, 'bar', 2] + * a1 = a.rotate(2) + * a1 # => [2, :foo, "bar"] + * + * If +count+ is large, uses <tt>count % array.size</tt> as the count: + * a = [:foo, 'bar', 2] + * a1 = a.rotate(20) + * a1 # => [2, :foo, "bar"] + * + * If +count+ is zero, returns a copy of +self+, unmodified: + * a = [:foo, 'bar', 2] + * a1 = a.rotate(0) + * a1 # => [:foo, "bar", 2] + * + * When given a negative \Integer +count+, rotates in the opposite direction, + * from end to beginning: + * a = [:foo, 'bar', 2] + * a1 = a.rotate(-2) + * a1 # => ["bar", 2, :foo] + * + * If +count+ is small (far from zero), uses <tt>count % array.size</tt> as the count: + * a = [:foo, 'bar', 2] + * a1 = a.rotate(-5) + * a1 # => ["bar", 2, :foo] */ static VALUE @@ -2693,6 +3201,7 @@ rb_ary_rotate_m(int argc, VALUE *argv, VALUE ary) struct ary_sort_data { VALUE ary; + VALUE receiver; struct cmp_opt_data cmp_opt; }; @@ -2705,6 +3214,15 @@ sort_reentered(VALUE ary) return Qnil; } +static void +sort_returned(struct ary_sort_data *data) +{ + if (rb_obj_frozen_p(data->receiver)) { + rb_raise(rb_eFrozenError, "array frozen during sort"); + } + sort_reentered(data->ary); +} + static int sort_1(const void *ap, const void *bp, void *dummy) { @@ -2718,7 +3236,7 @@ sort_1(const void *ap, const void *bp, void *dummy) args[1] = b; retval = rb_yield_values2(2, args); n = rb_cmpint(retval, a, b); - sort_reentered(data->ary); + sort_returned(data); return n; } @@ -2730,7 +3248,7 @@ sort_2(const void *ap, const void *bp, void *dummy) VALUE a = *(const VALUE *)ap, b = *(const VALUE *)bp; int n; - if (FIXNUM_P(a) && FIXNUM_P(b) && CMP_OPTIMIZABLE(data->cmp_opt, Fixnum)) { + if (FIXNUM_P(a) && FIXNUM_P(b) && CMP_OPTIMIZABLE(data->cmp_opt, Integer)) { if ((long)a > (long)b) return 1; if ((long)a < (long)b) return -1; return 0; @@ -2744,33 +3262,45 @@ sort_2(const void *ap, const void *bp, void *dummy) retval = rb_funcallv(a, id_cmp, 1, &b); n = rb_cmpint(retval, a, b); - sort_reentered(data->ary); + sort_returned(data); return n; } /* * call-seq: - * ary.sort! -> ary - * ary.sort! {|a, b| block} -> ary - * - * Sorts +self+ in place. - * - * Comparisons for the sort will be done using the <code><=></code> operator - * or using an optional code block. - * - * The block must implement a comparison between +a+ and +b+ and return - * an integer less than 0 when +b+ follows +a+, +0+ when +a+ and +b+ - * are equivalent, or an integer greater than 0 when +a+ follows +b+. - * - * The result is not guaranteed to be stable. When the comparison of two - * elements returns +0+, the order of the elements is unpredictable. - * - * ary = [ "d", "a", "e", "c", "b" ] - * ary.sort! #=> ["a", "b", "c", "d", "e"] - * ary.sort! {|a, b| b <=> a} #=> ["e", "d", "c", "b", "a"] - * - * See also Enumerable#sort_by. + * array.sort! -> self + * array.sort! {|a, b| ... } -> self + * + * Returns +self+ with its elements sorted in place. + * + * With no block, compares elements using operator <tt><=></tt> + * (see Comparable): + * a = 'abcde'.split('').shuffle + * a # => ["e", "b", "d", "a", "c"] + * a.sort! + * a # => ["a", "b", "c", "d", "e"] + * + * With a block, calls the block with each element pair; + * for each element pair +a+ and +b+, the block should return an integer: + * - Negative when +b+ is to follow +a+. + * - Zero when +a+ and +b+ are equivalent. + * - Positive when +a+ is to follow +b+. + * + * Example: + * a = 'abcde'.split('').shuffle + * a # => ["e", "b", "d", "a", "c"] + * a.sort! {|a, b| a <=> b } + * a # => ["a", "b", "c", "d", "e"] + * a.sort! {|a, b| b <=> a } + * a # => ["e", "d", "c", "b", "a"] + * + * When the block returns zero, the order for +a+ and +b+ is indeterminate, + * and may be unstable: + * a = 'abcde'.split('').shuffle + * a # => ["e", "b", "d", "a", "c"] + * a.sort! {|a, b| 0 } + * a # => ["d", "e", "c", "a", "b"] */ VALUE @@ -2784,6 +3314,7 @@ rb_ary_sort_bang(VALUE ary) long len = RARRAY_LEN(ary); RBASIC_CLEAR_CLASS(tmp); data.ary = tmp; + data.receiver = ary; data.cmp_opt.opt_methods = 0; data.cmp_opt.opt_inited = 0; RARRAY_PTR_USE(tmp, ptr, { @@ -2835,31 +3366,40 @@ rb_ary_sort_bang(VALUE ary) /* * call-seq: - * ary.sort -> new_ary - * ary.sort {|a, b| block} -> new_ary - * - * Returns a new array created by sorting +self+. - * - * Comparisons for the sort will be done using the <code><=></code> operator - * or using an optional code block. - * - * The block must implement a comparison between +a+ and +b+ and return - * an integer less than 0 when +b+ follows +a+, +0+ when +a+ and +b+ - * are equivalent, or an integer greater than 0 when +a+ follows +b+. - * - * The result is not guaranteed to be stable. When the comparison of two - * elements returns +0+, the order of the elements is unpredictable. - * - * ary = [ "d", "a", "e", "c", "b" ] - * ary.sort #=> ["a", "b", "c", "d", "e"] - * ary.sort {|a, b| b <=> a} #=> ["e", "d", "c", "b", "a"] - * - * To produce the reverse order, the following can also be used - * (and may be faster): - * - * ary.sort.reverse! #=> ["e", "d", "c", "b", "a"] - * - * See also Enumerable#sort_by. + * array.sort -> new_array + * array.sort {|a, b| ... } -> new_array + * + * Returns a new \Array whose elements are those from +self+, sorted. + * + * With no block, compares elements using operator <tt><=></tt> + * (see Comparable): + * a = 'abcde'.split('').shuffle + * a # => ["e", "b", "d", "a", "c"] + * a1 = a.sort + * a1 # => ["a", "b", "c", "d", "e"] + * + * With a block, calls the block with each element pair; + * for each element pair +a+ and +b+, the block should return an integer: + * - Negative when +b+ is to follow +a+. + * - Zero when +a+ and +b+ are equivalent. + * - Positive when +a+ is to follow +b+. + * + * Example: + * a = 'abcde'.split('').shuffle + * a # => ["e", "b", "d", "a", "c"] + * a1 = a.sort {|a, b| a <=> b } + * a1 # => ["a", "b", "c", "d", "e"] + * a2 = a.sort {|a, b| b <=> a } + * a2 # => ["e", "d", "c", "b", "a"] + * + * When the block returns zero, the order for +a+ and +b+ is indeterminate, + * and may be unstable: + * a = 'abcde'.split('').shuffle + * a # => ["e", "b", "d", "a", "c"] + * a1 = a.sort {|a, b| 0 } + * a1 # => ["c", "e", "b", "d", "a"] + * + * Related: Enumerable#sort_by. */ VALUE @@ -2874,55 +3414,12 @@ static VALUE rb_ary_bsearch_index(VALUE ary); /* * call-seq: - * ary.bsearch {|x| block } -> elem - * - * By using binary search, finds a value from this array which meets - * the given condition in O(log n) where n is the size of the array. - * - * You can use this method in two modes: a find-minimum mode and - * a find-any mode. In either case, the elements of the array must be - * monotone (or sorted) with respect to the block. - * - * In find-minimum mode (this is a good choice for typical use cases), - * the block must always return true or false, and there must be an index i - * (0 <= i <= ary.size) so that: - * - * - the block returns false for any element whose index is less than - * i, and - * - the block returns true for any element whose index is greater - * than or equal to i. - * - * This method returns the i-th element. If i is equal to ary.size, - * it returns nil. - * - * ary = [0, 4, 7, 10, 12] - * ary.bsearch {|x| x >= 4 } #=> 4 - * ary.bsearch {|x| x >= 6 } #=> 7 - * ary.bsearch {|x| x >= -1 } #=> 0 - * ary.bsearch {|x| x >= 100 } #=> nil - * - * In find-any mode (this behaves like libc's bsearch(3)), the block - * must always return a number, and there must be two indices i and j - * (0 <= i <= j <= ary.size) so that: - * - * - the block returns a positive number for ary[k] if 0 <= k < i, - * - the block returns zero for ary[k] if i <= k < j, and - * - the block returns a negative number for ary[k] if - * j <= k < ary.size. - * - * Under this condition, this method returns any element whose index - * is within i...j. If i is equal to j (i.e., there is no element - * that satisfies the block), this method returns nil. - * - * ary = [0, 4, 7, 10, 12] - * # try to find v such that 4 <= v < 8 - * ary.bsearch {|x| 1 - x / 4 } #=> 4 or 7 - * # try to find v such that 8 <= v < 10 - * ary.bsearch {|x| 4 - x / 2 } #=> nil - * - * You must not mix the two modes at a time; the block must always - * return either true/false, or always return a number. It is - * undefined which value is actually picked up at each iteration. + * array.bsearch {|element| ... } -> object + * array.bsearch -> new_enumerator + * + * Returns an element from +self+ selected by a binary search. + * + * See {Binary Searching}[rdoc-ref:bsearch.rdoc]. */ static VALUE @@ -2938,15 +3435,11 @@ rb_ary_bsearch(VALUE ary) /* * call-seq: - * ary.bsearch_index {|x| block } -> int or nil + * array.bsearch_index {|element| ... } -> integer or nil + * array.bsearch_index -> new_enumerator * - * By using binary search, finds an index of a value from this array which - * meets the given condition in O(log n) where n is the size of the array. - * - * It supports two modes, depending on the nature of the block. They are - * exactly the same as in the case of the #bsearch method, with the only difference - * being that this method returns the index of the element instead of the - * element itself. For more details consult the documentation for #bsearch. + * Searches +self+ as described at method #bsearch, + * but returns the _index_ of the found element instead of the element itself. */ static VALUE @@ -2969,15 +3462,15 @@ rb_ary_bsearch_index(VALUE ary) satisfied = 1; smaller = 1; } - else if (v == Qfalse || v == Qnil) { + else if (!RTEST(v)) { smaller = 0; } else if (rb_obj_is_kind_of(v, rb_cNumeric)) { const VALUE zero = INT2FIX(0); switch (rb_cmpint(rb_funcallv(v, id_cmp, 1, &zero), v, zero)) { case 0: return INT2FIX(mid); - case 1: smaller = 1; break; - case -1: smaller = 0; + case 1: smaller = 0; break; + case -1: smaller = 1; } } else { @@ -3005,18 +3498,26 @@ sort_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, dummy)) /* * call-seq: - * ary.sort_by! {|obj| block} -> ary - * ary.sort_by! -> Enumerator + * array.sort_by! {|element| ... } -> self + * array.sort_by! -> new_enumerator + * + * Sorts the elements of +self+ in place, + * using an ordering determined by the block; returns self. + * + * Calls the block with each successive element; + * sorts elements based on the values returned from the block. * - * Sorts +self+ in place using a set of keys generated by mapping the - * values in +self+ through the given block. + * For duplicates returned by the block, the ordering is indeterminate, and may be unstable. * - * The result is not guaranteed to be stable. When two keys are equal, - * the order of the corresponding elements is unpredictable. + * This example sorts strings based on their sizes: + * a = ['aaaa', 'bbb', 'cc', 'd'] + * a.sort_by! {|element| element.size } + * a # => ["d", "cc", "bbb", "aaaa"] * - * If no block is given, an Enumerator is returned instead. + * Returns a new \Enumerator if no block given: * - * See also Enumerable#sort_by. + * a = ['aaaa', 'bbb', 'cc', 'd'] + * a.sort_by! # => #<Enumerator: ["aaaa", "bbb", "cc", "d"]:sort_by!> */ static VALUE @@ -3034,23 +3535,21 @@ rb_ary_sort_by_bang(VALUE ary) /* * call-seq: - * ary.collect {|item| block} -> new_ary - * ary.map {|item| block} -> new_ary - * ary.collect -> Enumerator - * ary.map -> Enumerator + * array.map {|element| ... } -> new_array + * array.map -> new_enumerator * - * Invokes the given block once for each element of +self+. + * Calls the block, if given, with each element of +self+; + * returns a new \Array whose elements are the return values from the block: + * a = [:foo, 'bar', 2] + * a1 = a.map {|element| element.class } + * a1 # => [Symbol, String, Integer] * - * Creates a new array containing the values returned by the block. + * Returns a new \Enumerator if no block given: + * a = [:foo, 'bar', 2] + * a1 = a.map + * a1 # => #<Enumerator: [:foo, "bar", 2]:map> * - * See also Enumerable#collect. - * - * If no block is given, an Enumerator is returned instead. - * - * a = [ "a", "b", "c", "d" ] - * a.collect {|x| x + "!"} #=> ["a!", "b!", "c!", "d!"] - * a.map.with_index {|x, i| x * i} #=> ["", "b", "cc", "ddd"] - * a #=> ["a", "b", "c", "d"] + * Array#collect is an alias for Array#map. */ static VALUE @@ -3070,23 +3569,20 @@ rb_ary_collect(VALUE ary) /* * call-seq: - * ary.collect! {|item| block } -> ary - * ary.map! {|item| block } -> ary - * ary.collect! -> Enumerator - * ary.map! -> Enumerator - * - * Invokes the given block once for each element of +self+, replacing the - * element with the value returned by the block. + * array.map! {|element| ... } -> self + * array.map! -> new_enumerator * - * See also Enumerable#collect. + * Calls the block, if given, with each element; + * replaces the element with the block's return value: + * a = [:foo, 'bar', 2] + * a.map! { |element| element.class } # => [Symbol, String, Integer] * - * If no block is given, an Enumerator is returned instead. + * Returns a new \Enumerator if no block given: + * a = [:foo, 'bar', 2] + * a1 = a.map! + * a1 # => #<Enumerator: [:foo, "bar", 2]:map!> * - * a = [ "a", "b", "c", "d" ] - * a.map! {|x| x + "!" } - * a #=> [ "a!", "b!", "c!", "d!" ] - * a.collect!.with_index {|x, i| x[0...i] } - * a #=> ["", "b", "c!", "d!"] + * Array#collect! is an alias for Array#map!. */ static VALUE @@ -3158,20 +3654,38 @@ append_values_at_single(VALUE result, VALUE ary, long olen, VALUE idx) /* * call-seq: - * ary.values_at(selector, ...) -> new_ary + * array.values_at(*indexes) -> new_array * - * Returns an array containing the elements in +self+ corresponding to the - * given +selector+(s). + * Returns a new \Array whose elements are the elements + * of +self+ at the given \Integer or \Range +indexes+. * - * The selectors may be either integer indices or ranges. + * For each positive +index+, returns the element at offset +index+: + * a = [:foo, 'bar', 2] + * a.values_at(0, 2) # => [:foo, 2] + * a.values_at(0..1) # => [:foo, "bar"] * - * See also Array#select. + * The given +indexes+ may be in any order, and may repeat: + * a = [:foo, 'bar', 2] + * a.values_at(2, 0, 1, 0, 2) # => [2, :foo, "bar", :foo, 2] + * a.values_at(1, 0..2) # => ["bar", :foo, "bar", 2] * - * a = %w{ a b c d e f } - * a.values_at(1, 3, 5) # => ["b", "d", "f"] - * a.values_at(1, 3, 5, 7) # => ["b", "d", "f", nil] - * a.values_at(-1, -2, -2, -7) # => ["f", "e", "e", nil] - * a.values_at(4..6, 3...6) # => ["e", "f", nil, "d", "e", "f"] + * Assigns +nil+ for an +index+ that is too large: + * a = [:foo, 'bar', 2] + * a.values_at(0, 3, 1, 3) # => [:foo, nil, "bar", nil] + * + * Returns a new empty \Array if no arguments given. + * + * For each negative +index+, counts backward from the end of the array: + * a = [:foo, 'bar', 2] + * a.values_at(-1, -3) # => [2, :foo] + * + * Assigns +nil+ for an +index+ that is too small: + * a = [:foo, 'bar', 2] + * a.values_at(0, -5, 1, -6, 2) # => [:foo, nil, "bar", nil, 2] + * + * The given +indexes+ may have a mixture of signs: + * a = [:foo, 'bar', 2] + * a.values_at(0, -2, 1, -1) # => [:foo, "bar", "bar", 2] */ static VALUE @@ -3189,22 +3703,19 @@ rb_ary_values_at(int argc, VALUE *argv, VALUE ary) /* * call-seq: - * ary.select {|item| block} -> new_ary - * ary.select -> Enumerator - * ary.filter {|item| block} -> new_ary - * ary.filter -> Enumerator - * - * Returns a new array containing all elements of +ary+ - * for which the given +block+ returns a true value. - * - * If no block is given, an Enumerator is returned instead. + * array.select {|element| ... } -> new_array + * array.select -> new_enumerator * - * [1,2,3,4,5].select {|num| num.even? } #=> [2, 4] + * Calls the block, if given, with each element of +self+; + * returns a new \Array containing those elements of +self+ + * for which the block returns a truthy value: + * a = [:foo, 'bar', 2, :bam] + * a1 = a.select {|element| element.to_s.start_with?('b') } + * a1 # => ["bar", :bam] * - * a = %w[ a b c d e f ] - * a.select {|v| v =~ /[aeiou]/ } #=> ["a", "e"] - * - * See also Enumerable#select. + * Returns a new \Enumerator if no block given: + * a = [:foo, 'bar', 2, :bam] + * a.select # => #<Enumerator: [:foo, "bar", 2, :bam]:select> * * Array#filter is an alias for Array#select. */ @@ -3258,6 +3769,7 @@ select_bang_ensure(VALUE a) if (i2 < len && i2 < i1) { long tail = 0; + rb_ary_modify(ary); if (i1 < len) { tail = len - i1; RARRAY_PTR_USE_TRANSIENT(ary, ptr, { @@ -3271,21 +3783,21 @@ select_bang_ensure(VALUE a) /* * call-seq: - * ary.select! {|item| block } -> ary or nil - * ary.select! -> Enumerator - * ary.filter! {|item| block } -> ary or nil - * ary.filter! -> Enumerator - * - * Invokes the given block passing in successive elements from +self+, - * deleting elements for which the block returns a +false+ value. + * array.select! {|element| ... } -> self or nil + * array.select! -> new_enumerator * - * The array may not be changed instantly every time the block is called. + * Calls the block, if given with each element of +self+; + * removes from +self+ those elements for which the block returns +false+ or +nil+. * - * If changes were made, it will return +self+, otherwise it returns +nil+. + * Returns +self+ if any elements were removed: + * a = [:foo, 'bar', 2, :bam] + * a.select! {|element| element.to_s.start_with?('b') } # => ["bar", :bam] * - * If no block is given, an Enumerator is returned instead. + * Returns +nil+ if no elements were removed. * - * See also Array#keep_if. + * Returns a new \Enumerator if no block given: + * a = [:foo, 'bar', 2, :bam] + * a.select! # => #<Enumerator: [:foo, "bar", 2, :bam]:select!> * * Array#filter! is an alias for Array#select!. */ @@ -3305,19 +3817,17 @@ rb_ary_select_bang(VALUE ary) /* * call-seq: - * ary.keep_if {|item| block} -> ary - * ary.keep_if -> Enumerator + * array.keep_if {|element| ... } -> self + * array.keep_if -> new_enumeration * - * Deletes every element of +self+ for which the given block evaluates to - * +false+, and returns +self+. + * Retains those elements for which the block returns a truthy value; + * deletes all other elements; returns +self+: + * a = [:foo, 'bar', 2, :bam] + * a.keep_if {|element| element.to_s.start_with?('b') } # => ["bar", :bam] * - * If no block is given, an Enumerator is returned instead. - * - * a = %w[ a b c d e f ] - * a.keep_if {|v| v =~ /[aeiou]/ } #=> ["a", "e"] - * a #=> ["a", "e"] - * - * See also Array#select!. + * Returns a new \Enumerator if no block given: + * a = [:foo, 'bar', 2, :bam] + * a.keep_if # => #<Enumerator: [:foo, "bar", 2, :bam]:keep_if> */ static VALUE @@ -3343,22 +3853,34 @@ ary_resize_smaller(VALUE ary, long len) /* * call-seq: - * ary.delete(obj) -> item or nil - * ary.delete(obj) {block} -> item or result of block - * - * Deletes all items from +self+ that are equal to +obj+. - * - * Returns the last deleted item, or +nil+ if no matching item is found. - * - * If the optional code block is given, the result of the block is returned if - * the item is not found. (To remove +nil+ elements and get an informative - * return value, use Array#compact!) - * - * a = [ "a", "b", "b", "b", "c" ] - * a.delete("b") #=> "b" - * a #=> ["a", "c"] - * a.delete("z") #=> nil - * a.delete("z") {"not found"} #=> "not found" + * array.delete(obj) -> deleted_object + * array.delete(obj) {|nosuch| ... } -> deleted_object or block_return + * + * Removes zero or more elements from +self+; returns +self+. + * + * When no block is given, + * removes from +self+ each element +ele+ such that <tt>ele == obj</tt>; + * returns the last deleted element: + * s1 = 'bar'; s2 = 'bar' + * a = [:foo, s1, 2, s2] + * a.delete('bar') # => "bar" + * a # => [:foo, 2] + * + * Returns +nil+ if no elements removed. + * + * When a block is given, + * removes from +self+ each element +ele+ such that <tt>ele == obj</tt>. + * + * If any such elements are found, ignores the block + * and returns the last deleted element: + * s1 = 'bar'; s2 = 'bar' + * a = [:foo, s1, 2, s2] + * deleted_obj = a.delete('bar') {|obj| fail 'Cannot happen' } + * a # => [:foo, 2] + * + * If no such elements are found, returns the block's return value: + * a = [:foo, 'bar', 2] + * a.delete(:nosuch) {|obj| "#{obj} not found" } # => "nosuch not found" */ VALUE @@ -3439,17 +3961,23 @@ rb_ary_delete_at(VALUE ary, long pos) /* * call-seq: - * ary.delete_at(index) -> obj or nil + * array.delete_at(index) -> deleted_object or nil * - * Deletes the element at the specified +index+, returning that element, or - * +nil+ if the +index+ is out of range. + * Deletes an element from +self+, per the given \Integer +index+. * - * See also Array#slice! + * When +index+ is non-negative, deletes the element at offset +index+: + * a = [:foo, 'bar', 2] + * a.delete_at(1) # => "bar" + * a # => [:foo, 2] * - * a = ["ant", "bat", "cat", "dog"] - * a.delete_at(2) #=> "cat" - * a #=> ["ant", "bat", "dog"] - * a.delete_at(99) #=> nil + * If index is too large, returns +nil+. + * + * When +index+ is negative, counts backward from the end of the array: + * a = [:foo, 'bar', 2] + * a.delete_at(-2) # => "bar" + * a # => [:foo, 2] + * + * If +index+ is too small (far from zero), returns nil. */ static VALUE @@ -3458,63 +3986,118 @@ rb_ary_delete_at_m(VALUE ary, VALUE pos) return rb_ary_delete_at(ary, NUM2LONG(pos)); } +static VALUE +ary_slice_bang_by_rb_ary_splice(VALUE ary, long pos, long len) +{ + const long orig_len = RARRAY_LEN(ary); + + if (len < 0) { + return Qnil; + } + else if (pos < -orig_len) { + return Qnil; + } + else if (pos < 0) { + pos += orig_len; + } + else if (orig_len < pos) { + return Qnil; + } + if (orig_len < pos + len) { + len = orig_len - pos; + } + if (len == 0) { + return rb_ary_new2(0); + } + else { + VALUE arg2 = rb_ary_new4(len, RARRAY_CONST_PTR_TRANSIENT(ary)+pos); + rb_ary_splice(ary, pos, len, 0, 0); + return arg2; + } +} + /* * call-seq: - * ary.slice!(index) -> obj or nil - * ary.slice!(start, length) -> new_ary or nil - * ary.slice!(range) -> new_ary or nil - * - * Deletes the element(s) given by an +index+ (optionally up to +length+ - * elements) or by a +range+. - * - * Returns the deleted object (or objects), or +nil+ if the +index+ is out of - * range. - * - * a = [ "a", "b", "c" ] - * a.slice!(1) #=> "b" - * a #=> ["a", "c"] - * a.slice!(-1) #=> "c" - * a #=> ["a"] - * a.slice!(100) #=> nil - * a #=> ["a"] + * array.slice!(n) -> object or nil + * array.slice!(start, length) -> new_array or nil + * array.slice!(range) -> new_array or nil + * + * Removes and returns elements from +self+. + * + * When the only argument is an \Integer +n+, + * removes and returns the _nth_ element in +self+: + * a = [:foo, 'bar', 2] + * a.slice!(1) # => "bar" + * a # => [:foo, 2] + * + * If +n+ is negative, counts backwards from the end of +self+: + * a = [:foo, 'bar', 2] + * a.slice!(-1) # => 2 + * a # => [:foo, "bar"] + * + * If +n+ is out of range, returns +nil+. + * + * When the only arguments are Integers +start+ and +length+, + * removes +length+ elements from +self+ beginning at offset +start+; + * returns the deleted objects in a new Array: + * a = [:foo, 'bar', 2] + * a.slice!(0, 2) # => [:foo, "bar"] + * a # => [2] + * + * If <tt>start + length</tt> exceeds the array size, + * removes and returns all elements from offset +start+ to the end: + * a = [:foo, 'bar', 2] + * a.slice!(1, 50) # => ["bar", 2] + * a # => [:foo] + * + * If <tt>start == a.size</tt> and +length+ is non-negative, + * returns a new empty \Array. + * + * If +length+ is negative, returns +nil+. + * + * When the only argument is a \Range object +range+, + * treats <tt>range.min</tt> as +start+ above and <tt>range.size</tt> as +length+ above: + * a = [:foo, 'bar', 2] + * a.slice!(1..2) # => ["bar", 2] + * a # => [:foo] + * + * If <tt>range.start == a.size</tt>, returns a new empty \Array. + * + * If <tt>range.start</tt> is larger than the array size, returns +nil+. + * + * If <tt>range.end</tt> is negative, counts backwards from the end of the array: + * a = [:foo, 'bar', 2] + * a.slice!(0..-2) # => [:foo, "bar"] + * a # => [2] + * + * If <tt>range.start</tt> is negative, + * calculates the start index backwards from the end of the array: + * a = [:foo, 'bar', 2] + * a.slice!(-2..2) # => ["bar", 2] + * a # => [:foo] */ static VALUE rb_ary_slice_bang(int argc, VALUE *argv, VALUE ary) { - VALUE arg1, arg2; - long pos, len, orig_len; + VALUE arg1; + long pos, len; rb_ary_modify_check(ary); + rb_check_arity(argc, 1, 2); + arg1 = argv[0]; + if (argc == 2) { pos = NUM2LONG(argv[0]); len = NUM2LONG(argv[1]); - delete_pos_len: - if (len < 0) return Qnil; - orig_len = RARRAY_LEN(ary); - if (pos < 0) { - pos += orig_len; - if (pos < 0) return Qnil; - } - else if (orig_len < pos) return Qnil; - if (orig_len < pos + len) { - len = orig_len - pos; - } - if (len == 0) return rb_ary_new2(0); - arg2 = rb_ary_new4(len, RARRAY_CONST_PTR_TRANSIENT(ary)+pos); - RBASIC_SET_CLASS(arg2, rb_obj_class(ary)); - rb_ary_splice(ary, pos, len, 0, 0); - return arg2; + return ary_slice_bang_by_rb_ary_splice(ary, pos, len); } - rb_check_arity(argc, 1, 2); - arg1 = argv[0]; - if (!FIXNUM_P(arg1)) { switch (rb_range_beg_len(arg1, &pos, &len, RARRAY_LEN(ary), 0)) { case Qtrue: /* valid range */ - goto delete_pos_len; + return ary_slice_bang_by_rb_ary_splice(ary, pos, len); case Qnil: /* invalid range */ return Qnil; @@ -3572,17 +4155,20 @@ ary_reject_bang(VALUE ary) /* * call-seq: - * ary.reject! {|item| block} -> ary or nil - * ary.reject! -> Enumerator + * array.reject! {|element| ... } -> self or nil + * array.reject! -> new_enumerator * - * Deletes every element of +self+ for which the block evaluates to +true+, - * if no changes were made returns +nil+. + * Removes each element for which the block returns a truthy value. * - * The array may not be changed instantly every time the block is called. + * Returns +self+ if any elements removed: + * a = [:foo, 'bar', 2, 'bat'] + * a.reject! {|element| element.to_s.start_with?('b') } # => [:foo, 2] * - * See also Enumerable#reject and Array#delete_if. + * Returns +nil+ if no elements removed. * - * If no block is given, an Enumerator is returned instead. + * Returns a new \Enumerator if no block given: + * a = [:foo, 'bar', 2] + * a.reject! # => #<Enumerator: [:foo, "bar", 2]:reject!> */ static VALUE @@ -3595,15 +4181,18 @@ rb_ary_reject_bang(VALUE ary) /* * call-seq: - * ary.reject {|item| block } -> new_ary - * ary.reject -> Enumerator - * - * Returns a new array containing the items in +self+ for which the given - * block is not +true+. The ordering of non-rejected elements is maintained. - * - * See also Array#delete_if - * - * If no block is given, an Enumerator is returned instead. + * array.reject {|element| ... } -> new_array + * array.reject -> new_enumerator + * + * Returns a new \Array whose elements are all those from +self+ + * for which the block returns +false+ or +nil+: + * a = [:foo, 'bar', 2, 'bat'] + * a1 = a.reject {|element| element.to_s.start_with?('b') } + * a1 # => [:foo, 2] + * + * Returns a new \Enumerator if no block given: + * a = [:foo, 'bar', 2] + * a.reject # => #<Enumerator: [:foo, "bar", 2]:reject> */ static VALUE @@ -3619,20 +4208,17 @@ rb_ary_reject(VALUE ary) /* * call-seq: - * ary.delete_if {|item| block} -> ary - * ary.delete_if -> Enumerator + * array.delete_if {|element| ... } -> self + * array.delete_if -> Enumerator * - * Deletes every element of +self+ for which block evaluates to +true+. + * Removes each element in +self+ for which the block returns a truthy value; + * returns +self+: + * a = [:foo, 'bar', 2, 'bat'] + * a.delete_if {|element| element.to_s.start_with?('b') } # => [:foo, 2] * - * The array is changed instantly every time the block is called, not after - * the iteration is over. - * - * See also Array#reject! - * - * If no block is given, an Enumerator is returned instead. - * - * scores = [ 97, 42, 75 ] - * scores.delete_if {|score| score < 80 } #=> [97] + * Returns a new \Enumerator if no block given: + * a = [:foo, 'bar', 2] + * a.delete_if # => #<Enumerator: [:foo, "bar", 2]:delete_if> */ static VALUE @@ -3648,10 +4234,9 @@ static VALUE take_i(RB_BLOCK_CALL_FUNC_ARGLIST(val, cbarg)) { VALUE *args = (VALUE *)cbarg; - if (args[1] == 0) rb_iter_break(); - else args[1]--; if (argc > 1) val = rb_ary_new4(argc, argv); rb_ary_push(args[0], val); + if (--args[1] == 0) rb_iter_break(); return Qnil; } @@ -3661,6 +4246,7 @@ take_items(VALUE obj, long n) VALUE result = rb_check_array_type(obj); VALUE args[2]; + if (n == 0) return result; if (!NIL_P(result)) return rb_ary_subseq(result, 0, n); result = rb_ary_new2(n); args[0] = result; args[1] = (VALUE)n; @@ -3673,26 +4259,51 @@ take_items(VALUE obj, long n) /* * call-seq: - * ary.zip(arg, ...) -> new_ary - * ary.zip(arg, ...) {|arr| block} -> nil - * - * Converts any arguments to arrays, then merges elements of +self+ with - * corresponding elements from each argument. - * - * This generates a sequence of <code>ary.size</code> _n_-element arrays, - * where _n_ is one more than the count of arguments. - * - * If the size of any argument is less than the size of the initial array, - * +nil+ values are supplied. - * - * If a block is given, it is invoked for each output +array+, otherwise an - * array of arrays is returned. - * - * a = [ 4, 5, 6 ] - * b = [ 7, 8, 9 ] - * [1, 2, 3].zip(a, b) #=> [[1, 4, 7], [2, 5, 8], [3, 6, 9]] - * [1, 2].zip(a, b) #=> [[1, 4, 7], [2, 5, 8]] - * a.zip([1, 2], [8]) #=> [[4, 1, 8], [5, 2, nil], [6, nil, nil]] + * array.zip(*other_arrays) -> new_array + * array.zip(*other_arrays) {|other_array| ... } -> nil + * + * When no block given, returns a new \Array +new_array+ of size <tt>self.size</tt> + * whose elements are Arrays. + * + * Each nested array <tt>new_array[n]</tt> is of size <tt>other_arrays.size+1</tt>, + * and contains: + * - The _nth_ element of +self+. + * - The _nth_ element of each of the +other_arrays+. + * + * If all +other_arrays+ and +self+ are the same size: + * a = [:a0, :a1, :a2, :a3] + * b = [:b0, :b1, :b2, :b3] + * c = [:c0, :c1, :c2, :c3] + * d = a.zip(b, c) + * d # => [[:a0, :b0, :c0], [:a1, :b1, :c1], [:a2, :b2, :c2], [:a3, :b3, :c3]] + * + * If any array in +other_arrays+ is smaller than +self+, + * fills to <tt>self.size</tt> with +nil+: + * a = [:a0, :a1, :a2, :a3] + * b = [:b0, :b1, :b2] + * c = [:c0, :c1] + * d = a.zip(b, c) + * d # => [[:a0, :b0, :c0], [:a1, :b1, :c1], [:a2, :b2, nil], [:a3, nil, nil]] + * + * If any array in +other_arrays+ is larger than +self+, + * its trailing elements are ignored: + * a = [:a0, :a1, :a2, :a3] + * b = [:b0, :b1, :b2, :b3, :b4] + * c = [:c0, :c1, :c2, :c3, :c4, :c5] + * d = a.zip(b, c) + * d # => [[:a0, :b0, :c0], [:a1, :b1, :c1], [:a2, :b2, :c2], [:a3, :b3, :c3]] + * + * When a block is given, calls the block with each of the sub-arrays (formed as above); returns nil + * a = [:a0, :a1, :a2, :a3] + * b = [:b0, :b1, :b2, :b3] + * c = [:c0, :c1, :c2, :c3] + * a.zip(b, c) {|sub_array| p sub_array} # => nil + * + * Output: + * [:a0, :b0, :c0] + * [:a1, :b1, :c1] + * [:a2, :b2, :c2] + * [:a3, :b3, :c3] */ static VALUE @@ -3755,15 +4366,12 @@ rb_ary_zip(int argc, VALUE *argv, VALUE ary) /* * call-seq: - * ary.transpose -> new_ary - * - * Assumes that +self+ is an array of arrays and transposes the rows and - * columns. + * array.transpose -> new_array * - * a = [[1,2], [3,4], [5,6]] - * a.transpose #=> [[1, 3, 5], [2, 4, 6]] - * - * If the length of the subarrays don't match, an IndexError is raised. + * Transposes the rows and columns in an \Array of Arrays; + * the nested Arrays must all be the same size: + * a = [[:a0, :a1], [:b0, :b1], [:c0, :c1]] + * a.transpose # => [[:a0, :b0, :c0], [:a1, :b1, :c1]] */ static VALUE @@ -3796,15 +4404,11 @@ rb_ary_transpose(VALUE ary) /* * call-seq: - * ary.replace(other_ary) -> ary - * ary.initialize_copy(other_ary) -> ary - * - * Replaces the contents of +self+ with the contents of +other_ary+, - * truncating or expanding if necessary. + * array.replace(other_array) -> self * - * a = [ "a", "b", "c", "d", "e" ] - * a.replace([ "x", "y", "z" ]) #=> ["x", "y", "z"] - * a #=> ["x", "y", "z"] + * Replaces the content of +self+ with the content of +other_array+; returns +self+: + * a = [:foo, 'bar', 2] + * a.replace(['foo', :bar, 3]) # => ["foo", :bar, 3] */ VALUE @@ -3850,12 +4454,11 @@ rb_ary_replace(VALUE copy, VALUE orig) /* * call-seq: - * ary.clear -> ary - * - * Removes all elements from +self+. + * array.clear -> self * - * a = [ "a", "b", "c", "d", "e" ] - * a.clear #=> [ ] + * Removes all elements from +self+: + * a = [:foo, 'bar', 2] + * a.clear # => [] */ VALUE @@ -3881,32 +4484,171 @@ rb_ary_clear(VALUE ary) /* * call-seq: - * ary.fill(obj) -> ary - * ary.fill(obj, start [, length]) -> ary - * ary.fill(obj, range) -> ary - * ary.fill {|index| block} -> ary - * ary.fill(start [, length]) {|index| block} -> ary - * ary.fill(range) {|index| block} -> ary - * - * The first three forms set the selected elements of +self+ (which - * may be the entire array) to +obj+. - * - * A +start+ of +nil+ is equivalent to zero. - * - * A +length+ of +nil+ is equivalent to the length of the array. - * - * The last three forms fill the array with the value of the given block, - * which is passed the absolute index of each element to be filled. - * - * Negative values of +start+ count from the end of the array, where +-1+ is - * the last element. - * - * a = [ "a", "b", "c", "d" ] - * a.fill("x") #=> ["x", "x", "x", "x"] - * a.fill("z", 2, 2) #=> ["x", "x", "z", "z"] - * a.fill("y", 0..1) #=> ["y", "y", "z", "z"] - * a.fill {|i| i*i} #=> [0, 1, 4, 9] - * a.fill(-2) {|i| i*i*i} #=> [0, 1, 8, 27] + * array.fill(obj) -> self + * array.fill(obj, start) -> self + * array.fill(obj, start, length) -> self + * array.fill(obj, range) -> self + * array.fill {|index| ... } -> self + * array.fill(start) {|index| ... } -> self + * array.fill(start, length) {|index| ... } -> self + * array.fill(range) {|index| ... } -> self + * + * Replaces specified elements in +self+ with specified objects; returns +self+. + * + * With argument +obj+ and no block given, replaces all elements with that one object: + * a = ['a', 'b', 'c', 'd'] + * a # => ["a", "b", "c", "d"] + * a.fill(:X) # => [:X, :X, :X, :X] + * + * With arguments +obj+ and \Integer +start+, and no block given, + * replaces elements based on the given start. + * + * If +start+ is in range (<tt>0 <= start < array.size</tt>), + * replaces all elements from offset +start+ through the end: + * a = ['a', 'b', 'c', 'd'] + * a.fill(:X, 2) # => ["a", "b", :X, :X] + * + * If +start+ is too large (<tt>start >= array.size</tt>), does nothing: + * a = ['a', 'b', 'c', 'd'] + * a.fill(:X, 4) # => ["a", "b", "c", "d"] + * a = ['a', 'b', 'c', 'd'] + * a.fill(:X, 5) # => ["a", "b", "c", "d"] + * + * If +start+ is negative, counts from the end (starting index is <tt>start + array.size</tt>): + * a = ['a', 'b', 'c', 'd'] + * a.fill(:X, -2) # => ["a", "b", :X, :X] + * + * If +start+ is too small (less than and far from zero), replaces all elements: + * a = ['a', 'b', 'c', 'd'] + * a.fill(:X, -6) # => [:X, :X, :X, :X] + * a = ['a', 'b', 'c', 'd'] + * a.fill(:X, -50) # => [:X, :X, :X, :X] + * + * With arguments +obj+, \Integer +start+, and \Integer +length+, and no block given, + * replaces elements based on the given +start+ and +length+. + * + * If +start+ is in range, replaces +length+ elements beginning at offset +start+: + * a = ['a', 'b', 'c', 'd'] + * a.fill(:X, 1, 1) # => ["a", :X, "c", "d"] + * + * If +start+ is negative, counts from the end: + * a = ['a', 'b', 'c', 'd'] + * a.fill(:X, -2, 1) # => ["a", "b", :X, "d"] + * + * If +start+ is large (<tt>start >= array.size</tt>), extends +self+ with +nil+: + * a = ['a', 'b', 'c', 'd'] + * a.fill(:X, 5, 0) # => ["a", "b", "c", "d", nil] + * a = ['a', 'b', 'c', 'd'] + * a.fill(:X, 5, 2) # => ["a", "b", "c", "d", nil, :X, :X] + * + * If +length+ is zero or negative, replaces no elements: + * a = ['a', 'b', 'c', 'd'] + * a.fill(:X, 1, 0) # => ["a", "b", "c", "d"] + * a.fill(:X, 1, -1) # => ["a", "b", "c", "d"] + * + * With arguments +obj+ and \Range +range+, and no block given, + * replaces elements based on the given range. + * + * If the range is positive and ascending (<tt>0 < range.begin <= range.end</tt>), + * replaces elements from <tt>range.begin</tt> to <tt>range.end</tt>: + * a = ['a', 'b', 'c', 'd'] + * a.fill(:X, (1..1)) # => ["a", :X, "c", "d"] + * + * If <tt>range.first</tt> is negative, replaces no elements: + * a = ['a', 'b', 'c', 'd'] + * a.fill(:X, (-1..1)) # => ["a", "b", "c", "d"] + * + * If <tt>range.last</tt> is negative, counts from the end: + * a = ['a', 'b', 'c', 'd'] + * a.fill(:X, (0..-2)) # => [:X, :X, :X, "d"] + * a = ['a', 'b', 'c', 'd'] + * a.fill(:X, (1..-2)) # => ["a", :X, :X, "d"] + * + * If <tt>range.last</tt> and <tt>range.last</tt> are both negative, + * both count from the end of the array: + * a = ['a', 'b', 'c', 'd'] + * a.fill(:X, (-1..-1)) # => ["a", "b", "c", :X] + * a = ['a', 'b', 'c', 'd'] + * a.fill(:X, (-2..-2)) # => ["a", "b", :X, "d"] + * + * With no arguments and a block given, calls the block with each index; + * replaces the corresponding element with the block's return value: + * a = ['a', 'b', 'c', 'd'] + * a.fill { |index| "new_#{index}" } # => ["new_0", "new_1", "new_2", "new_3"] + * + * With argument +start+ and a block given, calls the block with each index + * from offset +start+ to the end; replaces the corresponding element + * with the block's return value: + * + * If start is in range (<tt>0 <= start < array.size</tt>), + * replaces from offset +start+ to the end: + * a = ['a', 'b', 'c', 'd'] + * a.fill(1) { |index| "new_#{index}" } # => ["a", "new_1", "new_2", "new_3"] + * + * If +start+ is too large(<tt>start >= array.size</tt>), does nothing: + * a = ['a', 'b', 'c', 'd'] + * a.fill(4) { |index| fail 'Cannot happen' } # => ["a", "b", "c", "d"] + * a = ['a', 'b', 'c', 'd'] + * a.fill(4) { |index| fail 'Cannot happen' } # => ["a", "b", "c", "d"] + * + * If +start+ is negative, counts from the end: + * a = ['a', 'b', 'c', 'd'] + * a.fill(-2) { |index| "new_#{index}" } # => ["a", "b", "new_2", "new_3"] + * + * If start is too small (<tt>start <= -array.size</tt>, replaces all elements: + * a = ['a', 'b', 'c', 'd'] + * a.fill(-6) { |index| "new_#{index}" } # => ["new_0", "new_1", "new_2", "new_3"] + * a = ['a', 'b', 'c', 'd'] + * a.fill(-50) { |index| "new_#{index}" } # => ["new_0", "new_1", "new_2", "new_3"] + * + * With arguments +start+ and +length+, and a block given, + * calls the block for each index specified by start length; + * replaces the corresponding element with the block's return value. + * + * If +start+ is in range, replaces +length+ elements beginning at offset +start+: + * a = ['a', 'b', 'c', 'd'] + * a.fill(1, 1) { |index| "new_#{index}" } # => ["a", "new_1", "c", "d"] + * + * If start is negative, counts from the end: + * a = ['a', 'b', 'c', 'd'] + * a.fill(-2, 1) { |index| "new_#{index}" } # => ["a", "b", "new_2", "d"] + * + * If +start+ is large (<tt>start >= array.size</tt>), extends +self+ with +nil+: + * a = ['a', 'b', 'c', 'd'] + * a.fill(5, 0) { |index| "new_#{index}" } # => ["a", "b", "c", "d", nil] + * a = ['a', 'b', 'c', 'd'] + * a.fill(5, 2) { |index| "new_#{index}" } # => ["a", "b", "c", "d", nil, "new_5", "new_6"] + * + * If +length+ is zero or less, replaces no elements: + * a = ['a', 'b', 'c', 'd'] + * a.fill(1, 0) { |index| "new_#{index}" } # => ["a", "b", "c", "d"] + * a.fill(1, -1) { |index| "new_#{index}" } # => ["a", "b", "c", "d"] + * + * With arguments +obj+ and +range+, and a block given, + * calls the block with each index in the given range; + * replaces the corresponding element with the block's return value. + * + * If the range is positive and ascending (<tt>range 0 < range.begin <= range.end</tt>, + * replaces elements from <tt>range.begin</tt> to <tt>range.end</tt>: + * a = ['a', 'b', 'c', 'd'] + * a.fill(1..1) { |index| "new_#{index}" } # => ["a", "new_1", "c", "d"] + * + * If +range.first+ is negative, does nothing: + * a = ['a', 'b', 'c', 'd'] + * a.fill(-1..1) { |index| fail 'Cannot happen' } # => ["a", "b", "c", "d"] + * + * If <tt>range.last</tt> is negative, counts from the end: + * a = ['a', 'b', 'c', 'd'] + * a.fill(0..-2) { |index| "new_#{index}" } # => ["new_0", "new_1", "new_2", "d"] + * a = ['a', 'b', 'c', 'd'] + * a.fill(1..-2) { |index| "new_#{index}" } # => ["a", "new_1", "new_2", "d"] + * + * If <tt>range.first</tt> and <tt>range.last</tt> are both negative, + * both count from the end: + * a = ['a', 'b', 'c', 'd'] + * a.fill(-1..-1) { |index| "new_#{index}" } # => ["a", "b", "c", "new_3"] + * a = ['a', 'b', 'c', 'd'] + * a.fill(-2..-2) { |index| "new_#{index}" } # => ["a", "b", "new_2", "d"] */ static VALUE @@ -3975,25 +4717,14 @@ rb_ary_fill(int argc, VALUE *argv, VALUE ary) /* * call-seq: - * ary + other_ary -> new_ary + * array + other_array -> new_array * - * Concatenation --- Returns a new array built by concatenating the - * two arrays together to produce a third array. + * Returns a new \Array containing all elements of +array+ + * followed by all elements of +other_array+: + * a = [0, 1] + [2, 3] + * a # => [0, 1, 2, 3] * - * [ 1, 2, 3 ] + [ 4, 5 ] #=> [ 1, 2, 3, 4, 5 ] - * a = [ "a", "b", "c" ] - * c = a + [ "d", "e", "f" ] - * c #=> [ "a", "b", "c", "d", "e", "f" ] - * a #=> [ "a", "b", "c" ] - * - * Note that - * x += y - * is the same as - * x = x + y - * This means that it produces a new array. As a consequence, - * repeated use of <code>+=</code> on arrays can be quite inefficient. - * - * See also Array#concat. + * Related: #concat. */ VALUE @@ -4021,27 +4752,17 @@ ary_append(VALUE x, VALUE y) if (n > 0) { rb_ary_splice(x, RARRAY_LEN(x), 0, RARRAY_CONST_PTR_TRANSIENT(y), n); } + RB_GC_GUARD(y); return x; } /* * call-seq: - * ary.concat(other_ary1, other_ary2, ...) -> ary - * - * Appends the elements of <code>other_ary</code>s to +self+. - * - * [ "a", "b" ].concat( ["c", "d"]) #=> [ "a", "b", "c", "d" ] - * [ "a" ].concat( ["b"], ["c", "d"]) #=> [ "a", "b", "c", "d" ] - * [ "a" ].concat #=> [ "a" ] - * - * a = [ 1, 2, 3 ] - * a.concat( [ 4, 5 ]) - * a #=> [ 1, 2, 3, 4, 5 ] + * array.concat(*other_arrays) -> self * - * a = [ 1, 2 ] - * a.concat(a, a) #=> [1, 2, 1, 2, 1, 2] - * - * See also Array#+. + * Adds to +array+ all elements from each \Array in +other_arrays+; returns +self+: + * a = [0, 1] + * a.concat([2, 3], [4, 5]) # => [0, 1, 2, 3, 4, 5] */ static VALUE @@ -4073,19 +4794,17 @@ rb_ary_concat(VALUE x, VALUE y) /* * call-seq: - * ary * int -> new_ary - * ary * str -> new_string - * - * Repetition --- With a String argument, equivalent to - * <code>ary.join(str)</code>. - * - * Otherwise, returns a new array built by concatenating the +int+ copies of - * +self+. + * array * n -> new_array + * array * string_separator -> new_string * + * When non-negative argument \Integer +n+ is given, + * returns a new \Array built by concatenating the +n+ copies of +self+: + * a = ['x', 'y'] + * a * 3 # => ["x", "y", "x", "y", "x", "y"] * - * [ 1, 2, 3 ] * 3 #=> [ 1, 2, 3, 1, 2, 3, 1, 2, 3 ] - * [ 1, 2, 3 ] * "," #=> "1,2,3" - * + * When \String argument +string_separator+ is given, + * equivalent to <tt>array.join(string_separator)</tt>: + * [0, [0, 1], {foo: 0}] * ', ' # => "0, 0, 1, {:foo=>0}" */ static VALUE @@ -4102,7 +4821,7 @@ rb_ary_times(VALUE ary, VALUE times) len = NUM2LONG(times); if (len == 0) { - ary2 = ary_new(rb_obj_class(ary), 0); + ary2 = ary_new(rb_cArray, 0); goto out; } if (len < 0) { @@ -4113,7 +4832,7 @@ rb_ary_times(VALUE ary, VALUE times) } len *= RARRAY_LEN(ary); - ary2 = ary_new(rb_obj_class(ary), len); + ary2 = ary_new(rb_cArray, len); ARY_SET_LEN(ary2, len); ptr = RARRAY_CONST_PTR_TRANSIENT(ary); @@ -4134,22 +4853,16 @@ rb_ary_times(VALUE ary, VALUE times) /* * call-seq: - * ary.assoc(obj) -> element_ary or nil - * - * Searches through an array whose elements are also arrays comparing +obj+ - * with the first element of each contained array using <code>obj.==</code>. + * array.assoc(obj) -> found_array or nil * - * Returns the first contained array that matches (that is, the first - * associated array), or +nil+ if no match is found. + * Returns the first element in +self+ that is an \Array + * whose first element <tt>==</tt> +obj+: + * a = [{foo: 0}, [2, 4], [4, 5, 6], [4, 5]] + * a.assoc(4) # => [4, 5, 6] * - * See also Array#rassoc + * Returns +nil+ if no such element is found. * - * s1 = [ "colors", "red", "blue", "green" ] - * s2 = [ "letters", "a", "b", "c" ] - * s3 = "foo" - * a = [ s1, s2, s3 ] - * a.assoc("letters") #=> [ "letters", "a", "b", "c" ] - * a.assoc("foo") #=> nil + * Related: #rassoc. */ VALUE @@ -4169,20 +4882,16 @@ rb_ary_assoc(VALUE ary, VALUE key) /* * call-seq: - * ary.rassoc(obj) -> element_ary or nil - * - * Searches through the array whose elements are also arrays. + * array.rassoc(obj) -> found_array or nil * - * Compares +obj+ with the second element of each contained array using - * <code>obj.==</code>. + * Returns the first element in +self+ that is an \Array + * whose second element <tt>==</tt> +obj+: + * a = [{foo: 0}, [2, 4], [4, 5, 6], [4, 5]] + * a.rassoc(4) # => [2, 4] * - * Returns the first contained array that matches +obj+. + * Returns +nil+ if no such element is found. * - * See also Array#assoc. - * - * a = [ [ 1, "one"], [2, "two"], [3, "three"], ["ii", "two"] ] - * a.rassoc("two") #=> [2, "two"] - * a.rassoc("four") #=> nil + * Related: #assoc. */ VALUE @@ -4237,16 +4946,19 @@ recursive_equal(VALUE ary1, VALUE ary2, int recur) /* * call-seq: - * ary == other_ary -> bool + * array == other_array -> true or false * - * Equality --- Two arrays are equal if they contain the same number of - * elements and if each element is equal to (according to Object#==) the - * corresponding element in +other_ary+. + * Returns +true+ if both <tt>array.size == other_array.size</tt> + * and for each index +i+ in +array+, <tt>array[i] == other_array[i]</tt>: + * a0 = [:foo, 'bar', 2] + * a1 = [:foo, 'bar', 2.0] + * a1 == a0 # => true + * [] == [] # => true * - * [ "a", "c" ] == [ "a", "c", 7 ] #=> false - * [ "a", "c", 7 ] == [ "a", "c", 7 ] #=> true - * [ "a", "c", 7 ] == [ "a", "d", "f" ] #=> false + * Otherwise, returns +false+. * + * This method is different from method Array#eql?, + * which compares elements using <tt>Object#eql?</tt>. */ static VALUE @@ -4279,10 +4991,18 @@ recursive_eql(VALUE ary1, VALUE ary2, int recur) /* * call-seq: - * ary.eql?(other) -> true or false + * array.eql? other_array -> true or false + * + * Returns +true+ if +self+ and +other_array+ are the same size, + * and if, for each index +i+ in +self+, <tt>self[i].eql? other_array[i]</tt>: + * a0 = [:foo, 'bar', 2] + * a1 = [:foo, 'bar', 2] + * a1.eql?(a0) # => true + * + * Otherwise, returns +false+. * - * Returns +true+ if +self+ and +other+ are the same object, - * or are both arrays with the same content (according to Object#eql?). + * This method is different from method {Array#==}[#method-i-3D-3D], + * which compares using method <tt>Object#==</tt>. */ static VALUE @@ -4297,14 +5017,13 @@ rb_ary_eql(VALUE ary1, VALUE ary2) /* * call-seq: - * ary.hash -> integer + * array.hash -> integer * - * Compute a hash-code for this array. + * Returns the integer hash value for +self+. * - * Two arrays with the same content will have the same hash code (and will - * compare using #eql?). - * - * See also Object#hash. + * Two arrays with the same content will have the same hash code (and will compare using eql?): + * [0, 1, 2].hash == [0, 1, 2].hash # => true + * [0, 1, 2].hash == [0, 1, 3].hash # => false */ static VALUE @@ -4326,14 +5045,12 @@ rb_ary_hash(VALUE ary) /* * call-seq: - * ary.include?(object) -> true or false - * - * Returns +true+ if the given +object+ is present in +self+ (that is, if any - * element <code>==</code> +object+), otherwise returns +false+. + * array.include?(obj) -> true or false * - * a = [ "a", "b", "c" ] - * a.include?("b") #=> true - * a.include?("z") #=> false + * Returns +true+ if for some index +i+ in +self+, <tt>obj == self[i]</tt>; + * otherwise +false+: + * [0, 1, 2].include?(2) # => true + * [0, 1, 2].include?(3) # => false */ VALUE @@ -4388,32 +5105,24 @@ recursive_cmp(VALUE ary1, VALUE ary2, int recur) /* * call-seq: - * ary <=> other_ary -> -1, 0, +1 or nil - * - * Comparison --- Returns an integer (+-1+, +0+, or <code>+1</code>) if this - * array is less than, equal to, or greater than +other_ary+. + * array <=> other_array -> -1, 0, or 1 * - * Each object in each array is compared (using the <=> operator). + * Returns -1, 0, or 1 as +self+ is less than, equal to, or greater than +other_array+. + * For each index +i+ in +self+, evaluates <tt>result = self[i] <=> other_array[i]</tt>. * - * Arrays are compared in an "element-wise" manner; the first element of +ary+ - * is compared with the first one of +other_ary+ using the <=> operator, then - * each of the second elements, etc... - * As soon as the result of any such comparison is non zero (i.e. the two - * corresponding elements are not equal), that result is returned for the - * whole array comparison. + * Returns -1 if any result is -1: + * [0, 1, 2] <=> [0, 1, 3] # => -1 * - * If all the elements are equal, then the result is based on a comparison of - * the array lengths. Thus, two arrays are "equal" according to Array#<=> if, - * and only if, they have the same length and the value of each element is - * equal to the value of the corresponding element in the other array. - * - * +nil+ is returned if the +other_ary+ is not an array or if the comparison - * of two elements returned +nil+. - * - * [ "a", "a", "c" ] <=> [ "a", "b", "c" ] #=> -1 - * [ 1, 2, 3, 4, 5, 6 ] <=> [ 1, 2 ] #=> +1 - * [ 1, 2 ] <=> [ 1, :two ] #=> nil + * Returns 1 if any result is 1: + * [0, 1, 2] <=> [0, 1, 1] # => 1 * + * When all results are zero: + * - Returns -1 if +array+ is smaller than +other_array+: + * [0, 1, 2] <=> [0, 1, 2, 3] # => -1 + * - Returns 1 if +array+ is larger than +other_array+: + * [0, 1, 2] <=> [0, 1] # => 1 + * - Returns 0 if +array+ and +other_array+ are the same size: + * [0, 1, 2] <=> [0, 1, 2] # => 0 */ VALUE @@ -4494,25 +5203,17 @@ ary_recycle_hash(VALUE hash) /* * call-seq: - * ary - other_ary -> new_ary - * - * Array Difference - * - * Returns a new array that is a copy of the original array, removing all - * occurrences of any item that also appear in +other_ary+. The order is - * preserved from the original array. - * - * It compares elements using their #hash and #eql? methods for efficiency. - * - * [ 1, 1, 2, 2, 3, 3, 4, 5 ] - [ 1, 2, 4 ] #=> [ 3, 3, 5 ] + * array - other_array -> new_array * - * Note that while 1 and 2 were only present once in the array argument, and - * were present twice in the receiver array, all occurrences of each Integer are - * removed in the returned array. + * Returns a new \Array containing only those elements from +array+ + * that are not found in \Array +other_array+; + * items are compared using <tt>eql?</tt>; + * the order from +array+ is preserved: + * [0, 1, 1, 2, 1, 1, 3, 1, 1] - [1] # => [0, 2, 3] + * [0, 1, 2, 3] - [3, 0] # => [1, 2] + * [0, 1, 2] - [4] # => [0, 1, 2] * - * If you need set-like behavior, see the library class Set. - * - * See also Array#difference. + * Related: Array#difference. */ static VALUE @@ -4523,6 +5224,7 @@ rb_ary_diff(VALUE ary1, VALUE ary2) long i; ary2 = to_ary(ary2); + if (RARRAY_LEN(ary2) == 0) { return ary_make_shared_copy(ary1); } ary3 = rb_ary_new(); if (RARRAY_LEN(ary1) <= SMALL_ARRAY_LEN || RARRAY_LEN(ary2) <= SMALL_ARRAY_LEN) { @@ -4545,31 +5247,18 @@ rb_ary_diff(VALUE ary1, VALUE ary2) /* * call-seq: - * ary.difference(other_ary1, other_ary2, ...) -> new_ary - * - * Array Difference - * - * Returns a new array that is a copy of the original array, removing all - * occurrences of any item that also appear in +other_ary+. The order is - * preserved from the original array. - * - * It compares elements using their #hash and #eql? methods for efficiency. - * - * [ 1, 1, 2, 2, 3, 3, 4, 5 ].difference([ 1, 2, 4 ]) #=> [ 3, 3, 5 ] - * - * Note that while 1 and 2 were only present once in the array argument, and - * were present twice in the receiver array, all occurrences of each Integer are - * removed in the returned array. + * array.difference(*other_arrays) -> new_array * - * Multiple array arguments can be supplied and all occurrences of any element - * in those supplied arrays that match the receiver will be removed from the - * returned array. + * Returns a new \Array containing only those elements from +self+ + * that are not found in any of the Arrays +other_arrays+; + * items are compared using <tt>eql?</tt>; order from +self+ is preserved: + * [0, 1, 1, 2, 1, 1, 3, 1, 1].difference([1]) # => [0, 2, 3] + * [0, 1, 2, 3].difference([3, 0], [1, 3]) # => [2] + * [0, 1, 2].difference([4]) # => [0, 1, 2] * - * [ 1, 'c', :s, 'yep' ].difference([ 1 ], [ 'a', 'c' ]) #=> [ :s, "yep" ] + * Returns a copy of +self+ if no arguments given. * - * If you need set-like behavior, see the library class Set. - * - * See also Array#-. + * Related: Array#-. */ static VALUE @@ -4611,17 +5300,17 @@ rb_ary_difference_multi(int argc, VALUE *argv, VALUE ary) /* * call-seq: - * ary & other_ary -> new_ary - * - * Set Intersection --- Returns a new array containing unique elements common to the - * two arrays. The order is preserved from the original array. + * array & other_array -> new_array * - * It compares elements using their #hash and #eql? methods for efficiency. + * Returns a new \Array containing each element found in both +array+ and \Array +other_array+; + * duplicates are omitted; items are compared using <tt>eql?</tt>: + * [0, 1, 2, 3] & [1, 2] # => [1, 2] + * [0, 1, 0, 1] & [0, 1] # => [0, 1] * - * [ 1, 1, 3, 5 ] & [ 3, 2, 1 ] #=> [ 1, 3 ] - * [ 'a', 'b', 'b', 'z' ] & [ 'a', 'b', 'c' ] #=> [ 'a', 'b' ] + * Preserves order from +array+: + * [0, 1, 2] & [3, 2, 1, 0] # => [0, 1, 2] * - * See also Array#uniq. + * Related: Array#intersection. */ @@ -4662,19 +5351,20 @@ rb_ary_and(VALUE ary1, VALUE ary2) /* * call-seq: - * ary.intersection(other_ary1, other_ary2, ...) -> new_ary + * array.intersection(*other_arrays) -> new_array * - * Set Intersection --- Returns a new array containing unique elements common - * to +self+ and <code>other_ary</code>s. Order is preserved from the original - * array. + * Returns a new \Array containing each element found both in +self+ + * and in all of the given Arrays +other_arrays+; + * duplicates are omitted; items are compared using <tt>eql?</tt>: + * [0, 1, 2, 3].intersection([0, 1, 2], [0, 1, 3]) # => [0, 1] + * [0, 0, 1, 1, 2, 3].intersection([0, 1, 2], [0, 1, 3]) # => [0, 1] * - * It compares elements using their #hash and #eql? methods for efficiency. + * Preserves order from +self+: + * [0, 1, 2].intersection([2, 1, 0]) # => [0, 1, 2] * - * [ 1, 1, 3, 5 ].intersection([ 3, 2, 1 ]) # => [ 1, 3 ] - * [ "a", "b", "z" ].intersection([ "a", "b", "c" ], [ "b" ]) # => [ "b" ] - * [ "a" ].intersection #=> [ "a" ] + * Returns a copy of +self+ if no arguments given. * - * See also Array#&. + * Related: Array#&. */ static VALUE @@ -4723,17 +5413,16 @@ rb_ary_union_hash(VALUE hash, VALUE ary2) /* * call-seq: - * ary | other_ary -> new_ary - * - * Set Union --- Returns a new array by joining +ary+ with +other_ary+, - * excluding any duplicates and preserving the order from the given arrays. + * array | other_array -> new_array * - * It compares elements using their #hash and #eql? methods for efficiency. + * Returns the union of +array+ and \Array +other_array+; + * duplicates are removed; order is preserved; + * items are compared using <tt>eql?</tt>: + * [0, 1] | [2, 3] # => [0, 1, 2, 3] + * [0, 1, 1] | [2, 2, 3] # => [0, 1, 2, 3] + * [0, 1, 2] | [3, 2, 1, 0] # => [0, 1, 2, 3] * - * [ "a", "b", "c" ] | [ "c", "d", "a" ] #=> [ "a", "b", "c", "d" ] - * [ "c", "d", "a" ] | [ "a", "b", "c" ] #=> [ "c", "d", "a", "b" ] - * - * See also Array#union. + * Related: Array#union. */ static VALUE @@ -4759,18 +5448,17 @@ rb_ary_or(VALUE ary1, VALUE ary2) /* * call-seq: - * ary.union(other_ary1, other_ary2, ...) -> new_ary - * - * Set Union --- Returns a new array by joining <code>other_ary</code>s with +self+, - * excluding any duplicates and preserving the order from the given arrays. + * array.union(*other_arrays) -> new_array * - * It compares elements using their #hash and #eql? methods for efficiency. + * Returns a new \Array that is the union of +self+ and all given Arrays +other_arrays+; + * duplicates are removed; order is preserved; items are compared using <tt>eql?</tt>: + * [0, 1, 2, 3].union([4, 5], [6, 7]) # => [0, 1, 2, 3, 4, 5, 6, 7] + * [0, 1, 1].union([2, 1], [3, 1]) # => [0, 1, 2, 3] + * [0, 1, 2, 3].union([3, 2], [1, 0]) # => [0, 1, 2, 3] * - * [ "a", "b", "c" ].union( [ "c", "d", "a" ] ) #=> [ "a", "b", "c", "d" ] - * [ "a" ].union( ["e", "b"], ["a", "c", "b"] ) #=> [ "a", "e", "b", "c" ] - * [ "a" ].union #=> [ "a" ] + * Returns a copy of +self+ if no arguments given. * - * See also Array#|. + * Related: Array#|. */ static VALUE @@ -4805,25 +5493,180 @@ rb_ary_union_multi(int argc, VALUE *argv, VALUE ary) /* * call-seq: - * ary.max -> obj - * ary.max {|a, b| block} -> obj - * ary.max(n) -> array - * ary.max(n) {|a, b| block} -> array + * ary.intersect?(other_ary) -> true or false * - * Returns the object in _ary_ with the maximum value. The - * first form assumes all objects implement Comparable; - * the second uses the block to return <em>a <=> b</em>. + * Returns +true+ if the array and +other_ary+ have at least one element in + * common, otherwise returns +false+. * - * ary = %w(albatross dog horse) - * ary.max #=> "horse" - * ary.max {|a, b| a.length <=> b.length} #=> "albatross" + * a = [ 1, 2, 3 ] + * b = [ 3, 4, 5 ] + * c = [ 5, 6, 7 ] + * a.intersect?(b) #=> true + * a.intersect?(c) #=> false + */ + +static VALUE +rb_ary_intersect_p(VALUE ary1, VALUE ary2) +{ + VALUE hash, v, result, shorter, longer; + st_data_t vv; + long i; + + ary2 = to_ary(ary2); + if (RARRAY_LEN(ary1) == 0 || RARRAY_LEN(ary2) == 0) return Qfalse; + + if (RARRAY_LEN(ary1) <= SMALL_ARRAY_LEN && RARRAY_LEN(ary2) <= SMALL_ARRAY_LEN) { + for (i=0; i<RARRAY_LEN(ary1); i++) { + v = RARRAY_AREF(ary1, i); + if (rb_ary_includes_by_eql(ary2, v)) return Qtrue; + } + return Qfalse; + } + + shorter = ary1; + longer = ary2; + if (RARRAY_LEN(ary1) > RARRAY_LEN(ary2)) { + longer = ary1; + shorter = ary2; + } + + hash = ary_make_hash(shorter); + result = Qfalse; + + for (i=0; i<RARRAY_LEN(longer); i++) { + v = RARRAY_AREF(longer, i); + vv = (st_data_t)v; + if (rb_hash_stlike_lookup(hash, vv, 0)) { + result = Qtrue; + break; + } + } + ary_recycle_hash(hash); + + return result; +} + +static VALUE +ary_max_generic(VALUE ary, long i, VALUE vmax) +{ + RUBY_ASSERT(i > 0 && i < RARRAY_LEN(ary)); + + VALUE v; + for (; i < RARRAY_LEN(ary); ++i) { + v = RARRAY_AREF(ary, i); + + if (rb_cmpint(rb_funcallv(vmax, id_cmp, 1, &v), vmax, v) < 0) { + vmax = v; + } + } + + return vmax; +} + +static VALUE +ary_max_opt_fixnum(VALUE ary, long i, VALUE vmax) +{ + const long n = RARRAY_LEN(ary); + RUBY_ASSERT(i > 0 && i < n); + RUBY_ASSERT(FIXNUM_P(vmax)); + + VALUE v; + for (; i < n; ++i) { + v = RARRAY_AREF(ary, i); + + if (FIXNUM_P(v)) { + if ((long)vmax < (long)v) { + vmax = v; + } + } + else { + return ary_max_generic(ary, i, vmax); + } + } + + return vmax; +} + +static VALUE +ary_max_opt_float(VALUE ary, long i, VALUE vmax) +{ + const long n = RARRAY_LEN(ary); + RUBY_ASSERT(i > 0 && i < n); + RUBY_ASSERT(RB_FLOAT_TYPE_P(vmax)); + + VALUE v; + for (; i < n; ++i) { + v = RARRAY_AREF(ary, i); + + if (RB_FLOAT_TYPE_P(v)) { + if (rb_float_cmp(vmax, v) < 0) { + vmax = v; + } + } + else { + return ary_max_generic(ary, i, vmax); + } + } + + return vmax; +} + +static VALUE +ary_max_opt_string(VALUE ary, long i, VALUE vmax) +{ + const long n = RARRAY_LEN(ary); + RUBY_ASSERT(i > 0 && i < n); + RUBY_ASSERT(STRING_P(vmax)); + + VALUE v; + for (; i < n; ++i) { + v = RARRAY_AREF(ary, i); + + if (STRING_P(v)) { + if (rb_str_cmp(vmax, v) < 0) { + vmax = v; + } + } + else { + return ary_max_generic(ary, i, vmax); + } + } + + return vmax; +} + +/* + * call-seq: + * array.max -> element + * array.max {|a, b| ... } -> element + * array.max(n) -> new_array + * array.max(n) {|a, b| ... } -> new_array + * + * Returns one of the following: + * - The maximum-valued element from +self+. + * - A new \Array of maximum-valued elements selected from +self+. + * + * When no block is given, each element in +self+ must respond to method <tt><=></tt> + * with an \Integer. + * + * With no argument and no block, returns the element in +self+ + * having the maximum value per method <tt><=></tt>: + * [0, 1, 2].max # => 2 * - * If the +n+ argument is given, maximum +n+ elements are returned - * as an array. + * With an argument \Integer +n+ and no block, returns a new \Array with at most +n+ elements, + * in descending order per method <tt><=></tt>: + * [0, 1, 2, 3].max(3) # => [3, 2, 1] + * [0, 1, 2, 3].max(6) # => [3, 2, 1, 0] * - * ary = %w[albatross dog horse] - * ary.max(2) #=> ["horse", "dog"] - * ary.max(2) {|a, b| a.length <=> b.length } #=> ["albatross", "horse"] + * When a block is given, the block must return an \Integer. + * + * With a block and no argument, calls the block <tt>self.size-1</tt> times to compare elements; + * returns the element having the maximum value per the block: + * ['0', '00', '000'].max {|a, b| a.size <=> b.size } # => "000" + * + * With an argument +n+ and a block, returns a new \Array with at most +n+ elements, + * in descending order per the block: + * ['0', '00', '000'].max(2) {|a, b| a.size <=> b.size } # => ["000", "00"] */ static VALUE rb_ary_max(int argc, VALUE *argv, VALUE ary) @@ -4836,6 +5679,7 @@ rb_ary_max(int argc, VALUE *argv, VALUE ary) if (rb_check_arity(argc, 0, 1) && !NIL_P(num = argv[0])) return rb_nmin_run(ary, num, 0, 1, 1); + const long n = RARRAY_LEN(ary); if (rb_block_given_p()) { for (i = 0; i < RARRAY_LEN(ary); i++) { v = RARRAY_AREF(ary, i); @@ -4844,39 +5688,148 @@ rb_ary_max(int argc, VALUE *argv, VALUE ary) } } } - else { - for (i = 0; i < RARRAY_LEN(ary); i++) { - v = RARRAY_AREF(ary, i); - if (result == Qundef || OPTIMIZED_CMP(v, result, cmp_opt) > 0) { - result = v; - } - } + else if (n > 0) { + result = RARRAY_AREF(ary, 0); + if (n > 1) { + if (FIXNUM_P(result) && CMP_OPTIMIZABLE(cmp_opt, Integer)) { + return ary_max_opt_fixnum(ary, 1, result); + } + else if (STRING_P(result) && CMP_OPTIMIZABLE(cmp_opt, String)) { + return ary_max_opt_string(ary, 1, result); + } + else if (RB_FLOAT_TYPE_P(result) && CMP_OPTIMIZABLE(cmp_opt, Float)) { + return ary_max_opt_float(ary, 1, result); + } + else { + return ary_max_generic(ary, 1, result); + } + } } if (result == Qundef) return Qnil; return result; } +static VALUE +ary_min_generic(VALUE ary, long i, VALUE vmin) +{ + RUBY_ASSERT(i > 0 && i < RARRAY_LEN(ary)); + + VALUE v; + for (; i < RARRAY_LEN(ary); ++i) { + v = RARRAY_AREF(ary, i); + + if (rb_cmpint(rb_funcallv(vmin, id_cmp, 1, &v), vmin, v) > 0) { + vmin = v; + } + } + + return vmin; +} + +static VALUE +ary_min_opt_fixnum(VALUE ary, long i, VALUE vmin) +{ + const long n = RARRAY_LEN(ary); + RUBY_ASSERT(i > 0 && i < n); + RUBY_ASSERT(FIXNUM_P(vmin)); + + VALUE a; + for (; i < n; ++i) { + a = RARRAY_AREF(ary, i); + + if (FIXNUM_P(a)) { + if ((long)vmin > (long)a) { + vmin = a; + } + } + else { + return ary_min_generic(ary, i, vmin); + } + } + + return vmin; +} + +static VALUE +ary_min_opt_float(VALUE ary, long i, VALUE vmin) +{ + const long n = RARRAY_LEN(ary); + RUBY_ASSERT(i > 0 && i < n); + RUBY_ASSERT(RB_FLOAT_TYPE_P(vmin)); + + VALUE a; + for (; i < n; ++i) { + a = RARRAY_AREF(ary, i); + + if (RB_FLOAT_TYPE_P(a)) { + if (rb_float_cmp(vmin, a) > 0) { + vmin = a; + } + } + else { + return ary_min_generic(ary, i, vmin); + } + } + + return vmin; +} + +static VALUE +ary_min_opt_string(VALUE ary, long i, VALUE vmin) +{ + const long n = RARRAY_LEN(ary); + RUBY_ASSERT(i > 0 && i < n); + RUBY_ASSERT(STRING_P(vmin)); + + VALUE a; + for (; i < n; ++i) { + a = RARRAY_AREF(ary, i); + + if (STRING_P(a)) { + if (rb_str_cmp(vmin, a) > 0) { + vmin = a; + } + } + else { + return ary_min_generic(ary, i, vmin); + } + } + + return vmin; +} + /* * call-seq: - * ary.min -> obj - * ary.min {| a,b | block } -> obj - * ary.min(n) -> array - * ary.min(n) {| a,b | block } -> array + * array.min -> element + * array.min { |a, b| ... } -> element + * array.min(n) -> new_array + * array.min(n) { |a, b| ... } -> new_array + * + * Returns one of the following: + * - The minimum-valued element from +self+. + * - A new \Array of minimum-valued elements selected from +self+. + * + * When no block is given, each element in +self+ must respond to method <tt><=></tt> + * with an \Integer. * - * Returns the object in _ary_ with the minimum value. The - * first form assumes all objects implement Comparable; - * the second uses the block to return <em>a <=> b</em>. + * With no argument and no block, returns the element in +self+ + * having the minimum value per method <tt><=></tt>: + * [0, 1, 2].min # => 0 * - * ary = %w(albatross dog horse) - * ary.min #=> "albatross" - * ary.min {|a, b| a.length <=> b.length} #=> "dog" + * With \Integer argument +n+ and no block, returns a new \Array with at most +n+ elements, + * in ascending order per method <tt><=></tt>: + * [0, 1, 2, 3].min(3) # => [0, 1, 2] + * [0, 1, 2, 3].min(6) # => [0, 1, 2, 3] * - * If the +n+ argument is given, minimum +n+ elements are returned - * as an array. + * When a block is given, the block must return an Integer. * - * ary = %w[albatross dog horse] - * ary.min(2) #=> ["albatross", "dog"] - * ary.min(2) {|a, b| a.length <=> b.length } #=> ["dog", "horse"] + * With a block and no argument, calls the block <tt>self.size-1</tt> times to compare elements; + * returns the element having the minimum value per the block: + * ['0', '00', '000'].min { |a, b| a.size <=> b.size } # => "0" + * + * With an argument +n+ and a block, returns a new \Array with at most +n+ elements, + * in ascending order per the block: + * ['0', '00', '000'].min(2) {|a, b| a.size <=> b.size } # => ["0", "00"] */ static VALUE rb_ary_min(int argc, VALUE *argv, VALUE ary) @@ -4889,6 +5842,7 @@ rb_ary_min(int argc, VALUE *argv, VALUE ary) if (rb_check_arity(argc, 0, 1) && !NIL_P(num = argv[0])) return rb_nmin_run(ary, num, 0, 0, 1); + const long n = RARRAY_LEN(ary); if (rb_block_given_p()) { for (i = 0; i < RARRAY_LEN(ary); i++) { v = RARRAY_AREF(ary, i); @@ -4897,13 +5851,22 @@ rb_ary_min(int argc, VALUE *argv, VALUE ary) } } } - else { - for (i = 0; i < RARRAY_LEN(ary); i++) { - v = RARRAY_AREF(ary, i); - if (result == Qundef || OPTIMIZED_CMP(v, result, cmp_opt) < 0) { - result = v; - } - } + else if (n > 0) { + result = RARRAY_AREF(ary, 0); + if (n > 1) { + if (FIXNUM_P(result) && CMP_OPTIMIZABLE(cmp_opt, Integer)) { + return ary_min_opt_fixnum(ary, 1, result); + } + else if (STRING_P(result) && CMP_OPTIMIZABLE(cmp_opt, String)) { + return ary_min_opt_string(ary, 1, result); + } + else if (RB_FLOAT_TYPE_P(result) && CMP_OPTIMIZABLE(cmp_opt, Float)) { + return ary_min_opt_float(ary, 1, result); + } + else { + return ary_min_generic(ary, 1, result); + } + } } if (result == Qundef) return Qnil; return result; @@ -4911,14 +5874,23 @@ rb_ary_min(int argc, VALUE *argv, VALUE ary) /* * call-seq: - * ary.minmax -> [obj, obj] - * ary.minmax {| a,b | block } -> [obj, obj] - * - * Returns a two element array which contains the minimum and the - * maximum value in the array. - * - * Can be given an optional block to override the default comparison - * method <code>a <=> b</code>. + * array.minmax -> [min_val, max_val] + * array.minmax {|a, b| ... } -> [min_val, max_val] + * + * Returns a new 2-element \Array containing the minimum and maximum values + * from +self+, either per method <tt><=></tt> or per a given block:. + * + * When no block is given, each element in +self+ must respond to method <tt><=></tt> + * with an \Integer; + * returns a new 2-element \Array containing the minimum and maximum values + * from +self+, per method <tt><=></tt>: + * [0, 1, 2].minmax # => [0, 2] + * + * When a block is given, the block must return an \Integer; + * the block is called <tt>self.size-1</tt> times to compare elements; + * returns a new 2-element \Array containing the minimum and maximum values + * from +self+, per the block: + * ['0', '00', '000'].minmax {|a, b| a.size <=> b.size } # => ["0", "000"] */ static VALUE rb_ary_minmax(VALUE ary) @@ -4938,31 +5910,31 @@ push_value(st_data_t key, st_data_t val, st_data_t ary) /* * call-seq: - * ary.uniq! -> ary or nil - * ary.uniq! {|item| ...} -> ary or nil + * array.uniq! -> self or nil + * array.uniq! {|element| ... } -> self or nil * - * Removes duplicate elements from +self+. + * Removes duplicate elements from +self+, the first occurrence always being retained; + * returns +self+ if any elements removed, +nil+ otherwise. * - * If a block is given, it will use the return value of the block for - * comparison. + * With no block given, identifies and removes elements using method <tt>eql?</tt> + * to compare. * - * It compares values using their #hash and #eql? methods for efficiency. + * Returns +self+ if any elements removed: + * a = [0, 0, 1, 1, 2, 2] + * a.uniq! # => [0, 1, 2] * - * +self+ is traversed in order, and the first occurrence is kept. + * Returns +nil+ if no elements removed. * - * Returns +nil+ if no changes are made (that is, no duplicates are found). + * With a block given, calls the block for each element; + * identifies (using method <tt>eql?</tt>) and removes + * elements for which the block returns duplicate values. * - * a = [ "a", "a", "b", "b", "c" ] - * a.uniq! # => ["a", "b", "c"] - * - * b = [ "a", "b", "c" ] - * b.uniq! # => nil - * - * c = [["student","sam"], ["student","george"], ["teacher","matz"]] - * c.uniq! {|s| s.first} # => [["student", "sam"], ["teacher", "matz"]] + * Returns +self+ if any elements removed: + * a = ['a', 'aa', 'aaa', 'b', 'bb', 'bbb'] + * a.uniq! {|element| element.size } # => ['a', 'aa', 'aaa'] * + * Returns +nil+ if no elements removed. */ - static VALUE rb_ary_uniq_bang(VALUE ary) { @@ -4996,23 +5968,22 @@ rb_ary_uniq_bang(VALUE ary) /* * call-seq: - * ary.uniq -> new_ary - * ary.uniq {|item| ...} -> new_ary - * - * Returns a new array by removing duplicate values in +self+. - * - * If a block is given, it will use the return value of the block for comparison. - * - * It compares values using their #hash and #eql? methods for efficiency. - * - * +self+ is traversed in order, and the first occurrence is kept. - * - * a = [ "a", "a", "b", "b", "c" ] - * a.uniq # => ["a", "b", "c"] - * - * b = [["student","sam"], ["student","george"], ["teacher","matz"]] - * b.uniq {|s| s.first} # => [["student", "sam"], ["teacher", "matz"]] - * + * array.uniq -> new_array + * array.uniq {|element| ... } -> new_array + * + * Returns a new \Array containing those elements from +self+ that are not duplicates, + * the first occurrence always being retained. + * + * With no block given, identifies and omits duplicates using method <tt>eql?</tt> + * to compare. + * a = [0, 0, 1, 1, 2, 2] + * a.uniq # => [0, 1, 2] + * + * With a block given, calls the block for each element; + * identifies (using method <tt>eql?</tt>) and omits duplicate values, + * that is, those elements for which the block returns the same value: + * a = ['a', 'aa', 'aaa', 'b', 'bb', 'bbb'] + * a.uniq {|element| element.size } # => ["a", "aa", "aaa"] */ static VALUE @@ -5032,7 +6003,6 @@ rb_ary_uniq(VALUE ary) hash = ary_make_hash(ary); uniq = rb_hash_values(hash); } - RBASIC_SET_CLASS(uniq, rb_obj_class(ary)); if (hash) { ary_recycle_hash(hash); } @@ -5042,14 +6012,11 @@ rb_ary_uniq(VALUE ary) /* * call-seq: - * ary.compact! -> ary or nil - * - * Removes +nil+ elements from the array. + * array.compact! -> self or nil * - * Returns +nil+ if no changes were made, otherwise returns the array. + * Removes all +nil+ elements from +self+. * - * [ "a", nil, "b", nil, "c" ].compact! #=> [ "a", "b", "c" ] - * [ "a", "b", "c" ].compact! #=> nil + * Returns +self+ if any elements removed, otherwise +nil+. */ static VALUE @@ -5077,12 +6044,11 @@ rb_ary_compact_bang(VALUE ary) /* * call-seq: - * ary.compact -> new_ary - * - * Returns a copy of +self+ with all +nil+ elements removed. + * array.compact -> new_array * - * [ "a", nil, "b", nil, "c", nil ].compact - * #=> [ "a", "b", "c" ] + * Returns a new \Array containing all non-+nil+ elements from +self+: + * a = [nil, 0, nil, 1, nil, 2, nil] + * a.compact # => [0, 1, 2] */ static VALUE @@ -5095,23 +6061,26 @@ rb_ary_compact(VALUE ary) /* * call-seq: - * ary.count -> int - * ary.count(obj) -> int - * ary.count {|item| block} -> int + * array.count -> an_integer + * array.count(obj) -> an_integer + * array.count {|element| ... } -> an_integer * - * Returns the number of elements. + * Returns a count of specified elements. * - * If an argument is given, counts the number of elements which equal +obj+ - * using <code>==</code>. + * With no argument and no block, returns the count of all elements: + * [0, 1, 2].count # => 3 + * [].count # => 0 * - * If a block is given, counts the number of elements for which the block - * returns a true value. + * With argument +obj+, returns the count of elements <tt>==</tt> to +obj+: + * [0, 1, 2, 0.0].count(0) # => 2 + * [0, 1, 2].count(3) # => 0 * - * ary = [1, 2, 4, 2] - * ary.count #=> 4 - * ary.count(2) #=> 2 - * ary.count {|x| x%2 == 0} #=> 3 + * With no argument and a block given, calls the block with each element; + * returns the count of elements for which the block returns a truthy value: + * [0, 1, 2, 3].count {|element| element > 1} # => 2 * + * With argument +obj+ and a block given, issues a warning, ignores the block, + * and returns the count of elements <tt>==</tt> to +obj+: */ static VALUE @@ -5148,8 +6117,8 @@ static VALUE flatten(VALUE ary, int level) { long i; - VALUE stack, result, tmp, elt, vmemo; - st_table *memo; + VALUE stack, result, tmp = 0, elt, vmemo; + st_table *memo = 0; st_data_t id; for (i = 0; i < RARRAY_LEN(ary); i++) { @@ -5161,8 +6130,6 @@ flatten(VALUE ary, int level) } if (i == RARRAY_LEN(ary)) { return ary; - } else if (tmp == ary) { - rb_raise(rb_eArgError, "tried to flatten recursive array"); } result = ary_new(0, RARRAY_LEN(ary)); @@ -5173,12 +6140,14 @@ flatten(VALUE ary, int level) rb_ary_push(stack, ary); rb_ary_push(stack, LONG2NUM(i + 1)); - vmemo = rb_hash_new(); - RBASIC_CLEAR_CLASS(vmemo); - memo = st_init_numtable(); - rb_hash_st_table_set(vmemo, memo); - st_insert(memo, (st_data_t)ary, (st_data_t)Qtrue); - st_insert(memo, (st_data_t)tmp, (st_data_t)Qtrue); + if (level < 0) { + vmemo = rb_hash_new(); + RBASIC_CLEAR_CLASS(vmemo); + memo = st_init_numtable(); + rb_hash_st_table_set(vmemo, memo); + st_insert(memo, (st_data_t)ary, (st_data_t)Qtrue); + st_insert(memo, (st_data_t)tmp, (st_data_t)Qtrue); + } ary = tmp; i = 0; @@ -5192,20 +6161,24 @@ flatten(VALUE ary, int level) } tmp = rb_check_array_type(elt); if (RBASIC(result)->klass) { - RB_GC_GUARD(vmemo); - st_clear(memo); + if (memo) { + RB_GC_GUARD(vmemo); + st_clear(memo); + } rb_raise(rb_eRuntimeError, "flatten reentered"); } if (NIL_P(tmp)) { rb_ary_push(result, elt); } else { - id = (st_data_t)tmp; - if (st_is_member(memo, id)) { - st_clear(memo); - rb_raise(rb_eArgError, "tried to flatten recursive array"); + if (memo) { + id = (st_data_t)tmp; + if (st_is_member(memo, id)) { + st_clear(memo); + rb_raise(rb_eArgError, "tried to flatten recursive array"); + } + st_insert(memo, id, (st_data_t)Qtrue); } - st_insert(memo, id, (st_data_t)Qtrue); rb_ary_push(stack, ary); rb_ary_push(stack, LONG2NUM(i)); ary = tmp; @@ -5215,37 +6188,49 @@ flatten(VALUE ary, int level) if (RARRAY_LEN(stack) == 0) { break; } - id = (st_data_t)ary; - st_delete(memo, &id, 0); + if (memo) { + id = (st_data_t)ary; + st_delete(memo, &id, 0); + } tmp = rb_ary_pop(stack); i = NUM2LONG(tmp); ary = rb_ary_pop(stack); } - st_clear(memo); + if (memo) { + st_clear(memo); + } - RBASIC_SET_CLASS(result, rb_obj_class(ary)); + RBASIC_SET_CLASS(result, rb_cArray); return result; } /* * call-seq: - * ary.flatten! -> ary or nil - * ary.flatten!(level) -> ary or nil - * - * Flattens +self+ in place. - * - * Returns +nil+ if no modifications were made (i.e., the array contains no - * subarrays.) - * - * The optional +level+ argument determines the level of recursion to flatten. - * - * a = [ 1, 2, [3, [4, 5] ] ] - * a.flatten! #=> [1, 2, 3, 4, 5] - * a.flatten! #=> nil - * a #=> [1, 2, 3, 4, 5] - * a = [ 1, 2, [3, [4, 5] ] ] - * a.flatten!(1) #=> [1, 2, 3, [4, 5]] + * array.flatten! -> self or nil + * array.flatten!(level) -> self or nil + * + * Replaces each nested \Array in +self+ with the elements from that \Array; + * returns +self+ if any changes, +nil+ otherwise. + * + * With non-negative \Integer argument +level+, flattens recursively through +level+ levels: + * a = [ 0, [ 1, [2, 3], 4 ], 5 ] + * a.flatten!(1) # => [0, 1, [2, 3], 4, 5] + * a = [ 0, [ 1, [2, 3], 4 ], 5 ] + * a.flatten!(2) # => [0, 1, 2, 3, 4, 5] + * a = [ 0, [ 1, [2, 3], 4 ], 5 ] + * a.flatten!(3) # => [0, 1, 2, 3, 4, 5] + * [0, 1, 2].flatten!(1) # => nil + * + * With no argument, a +nil+ argument, or with negative argument +level+, flattens all levels: + * a = [ 0, [ 1, [2, 3], 4 ], 5 ] + * a.flatten! # => [0, 1, 2, 3, 4, 5] + * [0, 1, 2].flatten! # => nil + * a = [ 0, [ 1, [2, 3], 4 ], 5 ] + * a.flatten!(-1) # => [0, 1, 2, 3, 4, 5] + * a = [ 0, [ 1, [2, 3], 4 ], 5 ] + * a.flatten!(-2) # => [0, 1, 2, 3, 4, 5] + * [0, 1, 2].flatten!(-1) # => nil */ static VALUE @@ -5272,24 +6257,32 @@ rb_ary_flatten_bang(int argc, VALUE *argv, VALUE ary) /* * call-seq: - * ary.flatten -> new_ary - * ary.flatten(level) -> new_ary - * - * Returns a new array that is a one-dimensional flattening of +self+ - * (recursively). - * - * That is, for every element that is an array, extract its elements into - * the new array. - * - * The optional +level+ argument determines the level of recursion to - * flatten. - * - * s = [ 1, 2, 3 ] #=> [1, 2, 3] - * t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]] - * a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10] - * a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - * a = [ 1, 2, [3, [4, 5] ] ] - * a.flatten(1) #=> [1, 2, 3, [4, 5]] + * array.flatten -> new_array + * array.flatten(level) -> new_array + * + * Returns a new \Array that is a recursive flattening of +self+: + * - Each non-Array element is unchanged. + * - Each \Array is replaced by its individual elements. + * + * With non-negative \Integer argument +level+, flattens recursively through +level+ levels: + * a = [ 0, [ 1, [2, 3], 4 ], 5 ] + * a.flatten(0) # => [0, [1, [2, 3], 4], 5] + * a = [ 0, [ 1, [2, 3], 4 ], 5 ] + * a.flatten(1) # => [0, 1, [2, 3], 4, 5] + * a = [ 0, [ 1, [2, 3], 4 ], 5 ] + * a.flatten(2) # => [0, 1, 2, 3, 4, 5] + * a = [ 0, [ 1, [2, 3], 4 ], 5 ] + * a.flatten(3) # => [0, 1, 2, 3, 4, 5] + * + * With no argument, a +nil+ argument, or with negative argument +level+, flattens all levels: + * a = [ 0, [ 1, [2, 3], 4 ], 5 ] + * a.flatten # => [0, 1, 2, 3, 4, 5] + * [0, 1, 2].flatten # => [0, 1, 2] + * a = [ 0, [ 1, [2, 3], 4 ], 5 ] + * a.flatten(-1) # => [0, 1, 2, 3, 4, 5] + * a = [ 0, [ 1, [2, 3], 4 ], 5 ] + * a.flatten(-2) # => [0, 1, 2, 3, 4, 5] + * [0, 1, 2].flatten(-1) # => [0, 1, 2] */ static VALUE @@ -5311,45 +6304,13 @@ rb_ary_flatten(int argc, VALUE *argv, VALUE ary) return result; } -#define OPTHASH_GIVEN_P(opts) \ - (argc > 0 && !NIL_P((opts) = rb_check_hash_type(argv[argc-1])) && (--argc, 1)) -static ID id_random; - #define RAND_UPTO(max) (long)rb_random_ulong_limited((randgen), (max)-1) -/* - * call-seq: - * ary.shuffle! -> ary - * ary.shuffle!(random: rng) -> ary - * - * Shuffles elements in +self+ in place. - * - * a = [ 1, 2, 3 ] #=> [1, 2, 3] - * a.shuffle! #=> [2, 3, 1] - * a #=> [2, 3, 1] - * - * The optional +rng+ argument will be used as the random number generator. - * - * a.shuffle!(random: Random.new(1)) #=> [1, 3, 2] - */ - static VALUE -rb_ary_shuffle_bang(int argc, VALUE *argv, VALUE ary) +rb_ary_shuffle_bang(rb_execution_context_t *ec, VALUE ary, VALUE randgen) { - VALUE opts, randgen = rb_cRandom; long i, len; - if (OPTHASH_GIVEN_P(opts)) { - VALUE rnd; - ID keyword_ids[1]; - - keyword_ids[0] = id_random; - rb_get_kwargs(opts, keyword_ids, 0, 1, &rnd); - if (rnd != Qundef) { - randgen = rnd; - } - } - rb_check_arity(argc, 0, 0); rb_ary_modify(ary); i = len = RARRAY_LEN(ary); RARRAY_PTR_USE(ary, ptr, { @@ -5367,80 +6328,24 @@ rb_ary_shuffle_bang(int argc, VALUE *argv, VALUE ary) return ary; } - -/* - * call-seq: - * ary.shuffle -> new_ary - * ary.shuffle(random: rng) -> new_ary - * - * Returns a new array with elements of +self+ shuffled. - * - * a = [ 1, 2, 3 ] #=> [1, 2, 3] - * a.shuffle #=> [2, 3, 1] - * a #=> [1, 2, 3] - * - * The optional +rng+ argument will be used as the random number generator. - * - * a.shuffle(random: Random.new(1)) #=> [1, 3, 2] - */ - static VALUE -rb_ary_shuffle(int argc, VALUE *argv, VALUE ary) +rb_ary_shuffle(rb_execution_context_t *ec, VALUE ary, VALUE randgen) { ary = rb_ary_dup(ary); - rb_ary_shuffle_bang(argc, argv, ary); + rb_ary_shuffle_bang(ec, ary, randgen); return ary; } - -/* - * call-seq: - * 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 ensure that an element doesn't repeat itself unless the array - * already contained duplicate elements. - * - * If the array is empty the first form returns +nil+ and the second form - * returns an empty array. - * - * a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] - * a.sample #=> 7 - * a.sample(4) #=> [6, 4, 2, 5] - * - * The optional +rng+ argument will be used as the random number generator. - * - * a.sample(random: Random.new(1)) #=> 6 - * a.sample(4, random: Random.new(1)) #=> [6, 10, 9, 2] - */ - - static VALUE -rb_ary_sample(int argc, VALUE *argv, VALUE ary) +ary_sample(rb_execution_context_t *ec, VALUE ary, VALUE randgen, VALUE nv, VALUE to_array) { - VALUE nv, result; - VALUE opts, randgen = rb_cRandom; + VALUE result; long n, len, i, j, k, idx[10]; long rnds[numberof(idx)]; long memo_threshold; - if (OPTHASH_GIVEN_P(opts)) { - VALUE rnd; - ID keyword_ids[1]; - - keyword_ids[0] = id_random; - rb_get_kwargs(opts, keyword_ids, 0, 1, &rnd); - if (rnd != Qundef) { - randgen = rnd; - } - } len = RARRAY_LEN(ary); - if (rb_check_arity(argc, 0, 1) == 0) { + if (!to_array) { if (len < 2) i = 0; else @@ -5448,7 +6353,6 @@ rb_ary_sample(int argc, VALUE *argv, VALUE ary) return rb_ary_elt(ary, i); } - nv = argv[0]; n = NUM2LONG(nv); if (n < 0) rb_raise(rb_eArgError, "negative sample number"); if (n > len) n = len; @@ -5470,7 +6374,7 @@ rb_ary_sample(int argc, VALUE *argv, VALUE ary) return rb_ary_new_capa(0); case 1: i = rnds[0]; - return rb_ary_new_from_values(1, &RARRAY_AREF(ary, i)); + return rb_ary_new_from_args(1, RARRAY_AREF(ary, i)); case 2: i = rnds[0]; j = rnds[1]; @@ -5563,6 +6467,12 @@ rb_ary_sample(int argc, VALUE *argv, VALUE ary) } static VALUE +ary_sample0(rb_execution_context_t *ec, VALUE ary) +{ + return ary_sample(ec, ary, rb_cRandom, Qfalse, Qfalse); +} + +static VALUE rb_ary_cycle_size(VALUE self, VALUE args, VALUE eobj) { long mul; @@ -5571,7 +6481,7 @@ rb_ary_cycle_size(VALUE self, VALUE args, VALUE eobj) n = RARRAY_AREF(args, 0); } if (RARRAY_LEN(self) == 0) return INT2FIX(0); - if (n == Qnil) return DBL2NUM(HUGE_VAL); + if (NIL_P(n)) return DBL2NUM(HUGE_VAL); mul = NUM2LONG(n); if (mul <= 0) return INT2FIX(0); n = LONG2FIX(mul); @@ -5580,24 +6490,33 @@ rb_ary_cycle_size(VALUE self, VALUE args, VALUE eobj) /* * call-seq: - * ary.cycle(n=nil) {|obj| block} -> nil - * ary.cycle(n=nil) -> Enumerator - * - * Calls the given block for each element +n+ times or forever if +nil+ is - * given. - * - * Does nothing if a non-positive number is given or the array is empty. - * - * Returns +nil+ if the loop has finished without getting interrupted. - * - * If no block is given, an Enumerator is returned instead. - * - * a = ["a", "b", "c"] - * a.cycle {|x| puts x} # print, a, b, c, a, b, c,.. forever. - * a.cycle(2) {|x| puts x} # print, a, b, c, a, b, c. - * + * array.cycle {|element| ... } -> nil + * array.cycle(count) {|element| ... } -> nil + * array.cycle -> new_enumerator + * array.cycle(count) -> new_enumerator + * + * When called with positive \Integer argument +count+ and a block, + * calls the block with each element, then does so again, + * until it has done so +count+ times; returns +nil+: + * output = [] + * [0, 1].cycle(2) {|element| output.push(element) } # => nil + * output # => [0, 1, 0, 1] + * + * If +count+ is zero or negative, does not call the block: + * [0, 1].cycle(0) {|element| fail 'Cannot happen' } # => nil + * [0, 1].cycle(-1) {|element| fail 'Cannot happen' } # => nil + * + * When a block is given, and argument is omitted or +nil+, cycles forever: + * # Prints 0 and 1 forever. + * [0, 1].cycle {|element| puts element } + * [0, 1].cycle(nil) {|element| puts element } + * + * When no block is given, returns a new \Enumerator: + * + * [0, 1].cycle(2) # => #<Enumerator: [0, 1]:cycle(2)> + * [0, 1].cycle # => # => #<Enumerator: [0, 1]:cycle> + * [0, 1].cycle.first(5) # => [0, 1, 0, 1, 0] */ - static VALUE rb_ary_cycle(int argc, VALUE *argv, VALUE ary) { @@ -5691,7 +6610,7 @@ permute0(const long n, const long r, long *const p, char *const used, const VALU /* * Returns the product of from, from-1, ..., from - how_many + 1. - * http://en.wikipedia.org/wiki/Pochhammer_symbol + * https://en.wikipedia.org/wiki/Pochhammer_symbol */ static VALUE descending_factorial(long from, long how_many) @@ -5743,30 +6662,66 @@ rb_ary_permutation_size(VALUE ary, VALUE args, VALUE eobj) /* * call-seq: - * ary.permutation {|p| block} -> ary - * ary.permutation -> Enumerator - * ary.permutation(n) {|p| block} -> ary - * ary.permutation(n) -> Enumerator - * - * When invoked with a block, yield all permutations of length +n+ of the - * elements of the array, then return the array itself. - * - * If +n+ is not specified, yield all permutations of all elements. - * - * The implementation makes no guarantees about the order in which the - * permutations are yielded. - * - * If no block is given, an Enumerator is returned instead. - * - * Examples: - * - * a = [1, 2, 3] - * a.permutation.to_a #=> [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] - * a.permutation(1).to_a #=> [[1],[2],[3]] - * a.permutation(2).to_a #=> [[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]] - * a.permutation(3).to_a #=> [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] - * a.permutation(0).to_a #=> [[]] # one permutation of length 0 - * a.permutation(4).to_a #=> [] # no permutations of length 4 + * array.permutation {|element| ... } -> self + * array.permutation(n) {|element| ... } -> self + * array.permutation -> new_enumerator + * array.permutation(n) -> new_enumerator + * + * When invoked with a block, yield all permutations of elements of +self+; returns +self+. + * The order of permutations is indeterminate. + * + * When a block and an in-range positive \Integer argument +n+ (<tt>0 < n <= self.size</tt>) + * are given, calls the block with all +n+-tuple permutations of +self+. + * + * Example: + * a = [0, 1, 2] + * a.permutation(2) {|permutation| p permutation } + * Output: + * [0, 1] + * [0, 2] + * [1, 0] + * [1, 2] + * [2, 0] + * [2, 1] + * Another example: + * a = [0, 1, 2] + * a.permutation(3) {|permutation| p permutation } + * Output: + * [0, 1, 2] + * [0, 2, 1] + * [1, 0, 2] + * [1, 2, 0] + * [2, 0, 1] + * [2, 1, 0] + * + * When +n+ is zero, calls the block once with a new empty \Array: + * a = [0, 1, 2] + * a.permutation(0) {|permutation| p permutation } + * Output: + * [] + * + * When +n+ is out of range (negative or larger than <tt>self.size</tt>), + * does not call the block: + * a = [0, 1, 2] + * a.permutation(-1) {|permutation| fail 'Cannot happen' } + * a.permutation(4) {|permutation| fail 'Cannot happen' } + * + * When a block given but no argument, + * behaves the same as <tt>a.permutation(a.size)</tt>: + * a = [0, 1, 2] + * a.permutation {|permutation| p permutation } + * Output: + * [0, 1, 2] + * [0, 2, 1] + * [1, 0, 2] + * [1, 2, 0] + * [2, 0, 1] + * [2, 1, 0] + * + * Returns a new \Enumerator if no block given: + * a = [0, 1, 2] + * a.permutation # => #<Enumerator: [0, 1, 2]:permutation> + * a.permutation(2) # => #<Enumerator: [0, 1, 2]:permutation(2)> */ static VALUE @@ -5839,27 +6794,44 @@ rb_ary_combination_size(VALUE ary, VALUE args, VALUE eobj) /* * call-seq: - * ary.combination(n) {|c| block} -> ary - * ary.combination(n) -> Enumerator - * - * When invoked with a block, yields all combinations of length +n+ of elements - * from the array and then returns the array itself. - * - * The implementation makes no guarantees about the order in which the - * combinations are yielded. - * - * If no block is given, an Enumerator is returned instead. - * - * Examples: - * - * a = [1, 2, 3, 4] - * a.combination(1).to_a #=> [[1],[2],[3],[4]] - * a.combination(2).to_a #=> [[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]] - * a.combination(3).to_a #=> [[1,2,3],[1,2,4],[1,3,4],[2,3,4]] - * a.combination(4).to_a #=> [[1,2,3,4]] - * a.combination(0).to_a #=> [[]] # one combination of length 0 - * a.combination(5).to_a #=> [] # no combinations of length 5 - * + * array.combination(n) {|element| ... } -> self + * array.combination(n) -> new_enumerator + * + * Calls the block, if given, with combinations of elements of +self+; + * returns +self+. The order of combinations is indeterminate. + * + * When a block and an in-range positive \Integer argument +n+ (<tt>0 < n <= self.size</tt>) + * are given, calls the block with all +n+-tuple combinations of +self+. + * + * Example: + * a = [0, 1, 2] + * a.combination(2) {|combination| p combination } + * Output: + * [0, 1] + * [0, 2] + * [1, 2] + * + * Another example: + * a = [0, 1, 2] + * a.combination(3) {|combination| p combination } + * Output: + * [0, 1, 2] + * + * When +n+ is zero, calls the block once with a new empty \Array: + * a = [0, 1, 2] + * a1 = a.combination(0) {|combination| p combination } + * Output: + * [] + * + * When +n+ is out of range (negative or larger than <tt>self.size</tt>), + * does not call the block: + * a = [0, 1, 2] + * a.combination(-1) {|combination| fail 'Cannot happen' } + * a.combination(4) {|combination| fail 'Cannot happen' } + * + * Returns a new \Enumerator if no block given: + * a = [0, 1, 2] + * a.combination(2) # => #<Enumerator: [0, 1, 2]:combination(2)> */ static VALUE @@ -5946,27 +6918,59 @@ rb_ary_repeated_permutation_size(VALUE ary, VALUE args, VALUE eobj) /* * call-seq: - * ary.repeated_permutation(n) {|p| block} -> ary - * ary.repeated_permutation(n) -> Enumerator - * - * When invoked with a block, yield all repeated permutations of length +n+ of - * the elements of the array, then return the array itself. - * - * The implementation makes no guarantees about the order in which the repeated - * permutations are yielded. - * - * If no block is given, an Enumerator is returned instead. - * - * Examples: - * - * a = [1, 2] - * a.repeated_permutation(1).to_a #=> [[1], [2]] - * a.repeated_permutation(2).to_a #=> [[1,1],[1,2],[2,1],[2,2]] - * a.repeated_permutation(3).to_a #=> [[1,1,1],[1,1,2],[1,2,1],[1,2,2], - * # [2,1,1],[2,1,2],[2,2,1],[2,2,2]] - * a.repeated_permutation(0).to_a #=> [[]] # one permutation of length 0 + * array.repeated_permutation(n) {|permutation| ... } -> self + * array.repeated_permutation(n) -> new_enumerator + * + * Calls the block with each repeated permutation of length +n+ of the elements of +self+; + * each permutation is an \Array; + * returns +self+. The order of the permutations is indeterminate. + * + * When a block and a positive \Integer argument +n+ are given, calls the block with each + * +n+-tuple repeated permutation of the elements of +self+. + * The number of permutations is <tt>self.size**n</tt>. + * + * +n+ = 1: + * a = [0, 1, 2] + * a.repeated_permutation(1) {|permutation| p permutation } + * Output: + * [0] + * [1] + * [2] + * + * +n+ = 2: + * a.repeated_permutation(2) {|permutation| p permutation } + * Output: + * [0, 0] + * [0, 1] + * [0, 2] + * [1, 0] + * [1, 1] + * [1, 2] + * [2, 0] + * [2, 1] + * [2, 2] + * + * If +n+ is zero, calls the block once with an empty \Array. + * + * If +n+ is negative, does not call the block: + * a.repeated_permutation(-1) {|permutation| fail 'Cannot happen' } + * + * Returns a new \Enumerator if no block given: + * a = [0, 1, 2] + * a.repeated_permutation(2) # => #<Enumerator: [0, 1, 2]:permutation(2)> + * + * Using Enumerators, it's convenient to show the permutations and counts + * for some values of +n+: + * e = a.repeated_permutation(0) + * e.size # => 1 + * e.to_a # => [[]] + * e = a.repeated_permutation(1) + * e.size # => 3 + * e.to_a # => [[0], [1], [2]] + * e = a.repeated_permutation(2) + * e.size # => 9 + * e.to_a # => [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]] */ - static VALUE rb_ary_repeated_permutation(VALUE ary, VALUE num) { @@ -6036,29 +7040,55 @@ rb_ary_repeated_combination_size(VALUE ary, VALUE args, VALUE eobj) /* * call-seq: - * ary.repeated_combination(n) {|c| block} -> ary - * ary.repeated_combination(n) -> Enumerator - * - * When invoked with a block, yields all repeated combinations of length +n+ of - * elements from the array and then returns the array itself. - * - * The implementation makes no guarantees about the order in which the repeated - * combinations are yielded. - * - * If no block is given, an Enumerator is returned instead. - * - * Examples: - * - * a = [1, 2, 3] - * a.repeated_combination(1).to_a #=> [[1], [2], [3]] - * a.repeated_combination(2).to_a #=> [[1,1],[1,2],[1,3],[2,2],[2,3],[3,3]] - * a.repeated_combination(3).to_a #=> [[1,1,1],[1,1,2],[1,1,3],[1,2,2],[1,2,3], - * # [1,3,3],[2,2,2],[2,2,3],[2,3,3],[3,3,3]] - * a.repeated_combination(4).to_a #=> [[1,1,1,1],[1,1,1,2],[1,1,1,3],[1,1,2,2],[1,1,2,3], - * # [1,1,3,3],[1,2,2,2],[1,2,2,3],[1,2,3,3],[1,3,3,3], - * # [2,2,2,2],[2,2,2,3],[2,2,3,3],[2,3,3,3],[3,3,3,3]] - * a.repeated_combination(0).to_a #=> [[]] # one combination of length 0 - * + * array.repeated_combination(n) {|combination| ... } -> self + * array.repeated_combination(n) -> new_enumerator + * + * Calls the block with each repeated combination of length +n+ of the elements of +self+; + * each combination is an \Array; + * returns +self+. The order of the combinations is indeterminate. + * + * When a block and a positive \Integer argument +n+ are given, calls the block with each + * +n+-tuple repeated combination of the elements of +self+. + * The number of combinations is <tt>(n+1)(n+2)/2</tt>. + * + * +n+ = 1: + * a = [0, 1, 2] + * a.repeated_combination(1) {|combination| p combination } + * Output: + * [0] + * [1] + * [2] + * + * +n+ = 2: + * a.repeated_combination(2) {|combination| p combination } + * Output: + * [0, 0] + * [0, 1] + * [0, 2] + * [1, 1] + * [1, 2] + * [2, 2] + * + * If +n+ is zero, calls the block once with an empty \Array. + * + * If +n+ is negative, does not call the block: + * a.repeated_combination(-1) {|combination| fail 'Cannot happen' } + * + * Returns a new \Enumerator if no block given: + * a = [0, 1, 2] + * a.repeated_combination(2) # => #<Enumerator: [0, 1, 2]:combination(2)> + * + * Using Enumerators, it's convenient to show the combinations and counts + * for some values of +n+: + * e = a.repeated_combination(0) + * e.size # => 1 + * e.to_a # => [[]] + * e = a.repeated_combination(1) + * e.size # => 3 + * e.to_a # => [[0], [1], [2]] + * e = a.repeated_combination(2) + * e.size # => 6 + * e.to_a # => [[0, 0], [0, 1], [0, 2], [1, 1], [1, 2], [2, 2]] */ static VALUE @@ -6098,23 +7128,51 @@ rb_ary_repeated_combination(VALUE ary, VALUE num) /* * call-seq: - * ary.product(other_ary, ...) -> new_ary - * ary.product(other_ary, ...) {|p| block} -> ary - * - * Returns an array of all combinations of elements from all arrays. - * - * The length of the returned array is the product of the length of +self+ and - * the argument arrays. - * - * If given a block, #product will yield all combinations and return +self+ - * instead. - * - * [1,2,3].product([4,5]) #=> [[1,4],[1,5],[2,4],[2,5],[3,4],[3,5]] - * [1,2].product([1,2]) #=> [[1,1],[1,2],[2,1],[2,2]] - * [1,2].product([3,4],[5,6]) #=> [[1,3,5],[1,3,6],[1,4,5],[1,4,6], - * # [2,3,5],[2,3,6],[2,4,5],[2,4,6]] - * [1,2].product() #=> [[1],[2]] - * [1,2].product([]) #=> [] + * array.product(*other_arrays) -> new_array + * array.product(*other_arrays) {|combination| ... } -> self + * + * Computes and returns or yields all combinations of elements from all the Arrays, + * including both +self+ and +other_arrays+. + * - The number of combinations is the product of the sizes of all the arrays, + * including both +self+ and +other_arrays+. + * - The order of the returned combinations is indeterminate. + * + * When no block is given, returns the combinations as an \Array of Arrays: + * a = [0, 1, 2] + * a1 = [3, 4] + * a2 = [5, 6] + * p = a.product(a1) + * p.size # => 6 # a.size * a1.size + * p # => [[0, 3], [0, 4], [1, 3], [1, 4], [2, 3], [2, 4]] + * p = a.product(a1, a2) + * p.size # => 12 # a.size * a1.size * a2.size + * p # => [[0, 3, 5], [0, 3, 6], [0, 4, 5], [0, 4, 6], [1, 3, 5], [1, 3, 6], [1, 4, 5], [1, 4, 6], [2, 3, 5], [2, 3, 6], [2, 4, 5], [2, 4, 6]] + * + * If any argument is an empty \Array, returns an empty \Array. + * + * If no argument is given, returns an \Array of 1-element Arrays, + * each containing an element of +self+: + * a.product # => [[0], [1], [2]] + * + * When a block is given, yields each combination as an \Array; returns +self+: + * a.product(a1) {|combination| p combination } + * Output: + * [0, 3] + * [0, 4] + * [1, 3] + * [1, 4] + * [2, 3] + * [2, 4] + * + * If any argument is an empty \Array, does not call the block: + * a.product(a1, a2, []) {|combination| fail 'Cannot happen' } + * + * If no argument is given, yields each element of +self+ as a 1-element \Array: + * a.product {|combination| p combination } + * Output: + * [0] + * [1] + * [2] */ static VALUE @@ -6207,17 +7265,18 @@ done: /* * call-seq: - * ary.take(n) -> new_ary - * - * Returns first +n+ elements from the array. - * - * If a negative number is given, raises an ArgumentError. - * - * See also Array#drop - * - * a = [1, 2, 3, 4, 5, 0] - * a.take(3) #=> [1, 2, 3] - * + * array.take(n) -> new_array + * + * Returns a new \Array containing the first +n+ element of +self+, + * where +n+ is a non-negative \Integer; + * does not modify +self+. + * + * Examples: + * a = [0, 1, 2, 3, 4, 5] + * a.take(1) # => [0] + * a.take(2) # => [0, 1] + * a.take(50) # => [0, 1, 2, 3, 4, 5] + * a # => [0, 1, 2, 3, 4, 5] */ static VALUE @@ -6232,19 +7291,22 @@ rb_ary_take(VALUE obj, VALUE n) /* * call-seq: - * ary.take_while {|obj| block} -> new_ary - * ary.take_while -> Enumerator - * - * Passes elements to the block until the block returns +nil+ or +false+, then - * stops iterating and returns an array of all prior elements. - * - * If no block is given, an Enumerator is returned instead. - * - * See also Array#drop_while - * - * a = [1, 2, 3, 4, 5, 0] - * a.take_while {|i| i < 3} #=> [1, 2] - * + * array.take_while {|element| ... } -> new_array + * array.take_while -> new_enumerator + * + * Returns a new \Array containing zero or more leading elements of +self+; + * does not modify +self+. + * + * With a block given, calls the block with each successive element of +self+; + * stops if the block returns +false+ or +nil+; + * returns a new Array containing those elements for which the block returned a truthy value: + * a = [0, 1, 2, 3, 4, 5] + * a.take_while {|element| element < 3 } # => [0, 1, 2] + * a.take_while {|element| true } # => [0, 1, 2, 3, 4, 5] + * a # => [0, 1, 2, 3, 4, 5] + * + * With no block given, returns a new \Enumerator: + * [0, 1].take_while # => #<Enumerator: [0, 1]:take_while> */ static VALUE @@ -6261,18 +7323,17 @@ rb_ary_take_while(VALUE ary) /* * call-seq: - * ary.drop(n) -> new_ary - * - * Drops first +n+ elements from +ary+ and returns the rest of the elements in - * an array. + * array.drop(n) -> new_array * - * If a negative number is given, raises an ArgumentError. - * - * See also Array#take - * - * a = [1, 2, 3, 4, 5, 0] - * a.drop(3) #=> [4, 5, 0] + * Returns a new \Array containing all but the first +n+ element of +self+, + * where +n+ is a non-negative \Integer; + * does not modify +self+. * + * Examples: + * a = [0, 1, 2, 3, 4, 5] + * a.drop(0) # => [0, 1, 2, 3, 4, 5] + * a.drop(1) # => [1, 2, 3, 4, 5] + * a.drop(2) # => [2, 3, 4, 5] */ static VALUE @@ -6285,26 +7346,26 @@ rb_ary_drop(VALUE ary, VALUE n) } result = rb_ary_subseq(ary, pos, RARRAY_LEN(ary)); - if (result == Qnil) result = rb_ary_new(); + if (NIL_P(result)) result = rb_ary_new(); return result; } /* * call-seq: - * ary.drop_while {|obj| block} -> new_ary - * ary.drop_while -> Enumerator - * - * Drops elements up to, but not including, the first element for which the - * block returns +nil+ or +false+ and returns an array containing the - * remaining elements. - * - * If no block is given, an Enumerator is returned instead. - * - * See also Array#take_while + * array.drop_while {|element| ... } -> new_array + * array.drop_while -> new_enumerator + + * Returns a new \Array containing zero or more trailing elements of +self+; + * does not modify +self+. * - * a = [1, 2, 3, 4, 5, 0] - * a.drop_while {|i| i < 3 } #=> [3, 4, 5, 0] + * With a block given, calls the block with each successive element of +self+; + * stops if the block returns +false+ or +nil+; + * returns a new Array _omitting_ those elements for which the block returned a truthy value: + * a = [0, 1, 2, 3, 4, 5] + * a.drop_while {|element| element < 3 } # => [3, 4, 5] * + * With no block given, returns a new \Enumerator: + * [0, 1].drop_while # => # => #<Enumerator: [0, 1]:drop_while> */ static VALUE @@ -6321,10 +7382,32 @@ rb_ary_drop_while(VALUE ary) /* * call-seq: - * ary.any? [{|obj| block} ] -> true or false - * ary.any?(pattern) -> true or false - * - * See also Enumerable#any? + * array.any? -> true or false + * array.any? {|element| ... } -> true or false + * array.any?(obj) -> true or false + * + * Returns +true+ if any element of +self+ meets a given criterion. + * + * With no block given and no argument, returns +true+ if +self+ has any truthy element, + * +false+ otherwise: + * [nil, 0, false].any? # => true + * [nil, false].any? # => false + * [].any? # => false + * + * With a block given and no argument, calls the block with each element in +self+; + * returns +true+ if the block returns any truthy value, +false+ otherwise: + * [0, 1, 2].any? {|element| element > 1 } # => true + * [0, 1, 2].any? {|element| element > 2 } # => false + * + * If argument +obj+ is given, returns +true+ if +obj+.<tt>===</tt> any element, + * +false+ otherwise: + * ['food', 'drink'].any?(/foo/) # => true + * ['food', 'drink'].any?(/bar/) # => false + * [].any?(/foo/) # => false + * [0, 1, 2].any?(1) # => true + * [0, 1, 2].any?(3) # => false + * + * Related: Enumerable#any? */ static VALUE @@ -6357,10 +7440,31 @@ rb_ary_any_p(int argc, VALUE *argv, VALUE ary) /* * call-seq: - * ary.all? [{|obj| block} ] -> true or false - * ary.all?(pattern) -> true or false - * - * See also Enumerable#all? + * array.all? -> true or false + * array.all? {|element| ... } -> true or false + * array.all?(obj) -> true or false + * + * Returns +true+ if all elements of +self+ meet a given criterion. + * + * With no block given and no argument, returns +true+ if +self+ contains only truthy elements, + * +false+ otherwise: + * [0, 1, :foo].all? # => true + * [0, nil, 2].all? # => false + * [].all? # => true + * + * With a block given and no argument, calls the block with each element in +self+; + * returns +true+ if the block returns only truthy values, +false+ otherwise: + * [0, 1, 2].all? { |element| element < 3 } # => true + * [0, 1, 2].all? { |element| element < 2 } # => false + * + * If argument +obj+ is given, returns +true+ if <tt>obj.===</tt> every element, +false+ otherwise: + * ['food', 'fool', 'foot'].all?(/foo/) # => true + * ['food', 'drink'].all?(/bar/) # => false + * [].all?(/foo/) # => true + * [0, 0, 0].all?(0) # => true + * [0, 1, 2].all?(1) # => false + * + * Related: Enumerable#all? */ static VALUE @@ -6393,10 +7497,31 @@ rb_ary_all_p(int argc, VALUE *argv, VALUE ary) /* * call-seq: - * ary.none? [{|obj| block} ] -> true or false - * ary.none?(pattern) -> true or false - * - * See also Enumerable#none? + * array.none? -> true or false + * array.none? {|element| ... } -> true or false + * array.none?(obj) -> true or false + * + * Returns +true+ if no element of +self+ meet a given criterion. + * + * With no block given and no argument, returns +true+ if +self+ has no truthy elements, + * +false+ otherwise: + * [nil, false].none? # => true + * [nil, 0, false].none? # => false + * [].none? # => true + * + * With a block given and no argument, calls the block with each element in +self+; + * returns +true+ if the block returns no truthy value, +false+ otherwise: + * [0, 1, 2].none? {|element| element > 3 } # => true + * [0, 1, 2].none? {|element| element > 1 } # => false + * + * If argument +obj+ is given, returns +true+ if <tt>obj.===</tt> no element, +false+ otherwise: + * ['food', 'drink'].none?(/bar/) # => true + * ['food', 'drink'].none?(/foo/) # => false + * [].none?(/foo/) # => true + * [0, 1, 2].none?(3) # => true + * [0, 1, 2].none?(1) # => false + * + * Related: Enumerable#none? */ static VALUE @@ -6429,10 +7554,35 @@ rb_ary_none_p(int argc, VALUE *argv, VALUE ary) /* * call-seq: - * ary.one? [{|obj| block} ] -> true or false - * ary.one?(pattern) -> true or false - * - * See also Enumerable#one? + * array.one? -> true or false + * array.one? {|element| ... } -> true or false + * array.one?(obj) -> true or false + * + * Returns +true+ if exactly one element of +self+ meets a given criterion. + * + * With no block given and no argument, returns +true+ if +self+ has exactly one truthy element, + * +false+ otherwise: + * [nil, 0].one? # => true + * [0, 0].one? # => false + * [nil, nil].one? # => false + * [].one? # => false + * + * With a block given and no argument, calls the block with each element in +self+; + * returns +true+ if the block a truthy value for exactly one element, +false+ otherwise: + * [0, 1, 2].one? {|element| element > 0 } # => false + * [0, 1, 2].one? {|element| element > 1 } # => true + * [0, 1, 2].one? {|element| element > 2 } # => false + * + * If argument +obj+ is given, returns +true+ if <tt>obj.===</tt> exactly one element, + * +false+ otherwise: + * [0, 1, 2].one?(0) # => true + * [0, 0, 1].one?(0) # => false + * [1, 1, 2].one?(0) # => false + * ['food', 'drink'].one?(/bar/) # => false + * ['food', 'drink'].one?(/foo/) # => true + * [].one?(/foo/) # => false + * + * Related: Enumerable#one? */ static VALUE @@ -6474,19 +7624,20 @@ rb_ary_one_p(int argc, VALUE *argv, VALUE ary) } /* - * call-seq: - * ary.dig(idx, ...) -> object - * - * Extracts the nested value specified by the sequence of <i>idx</i> - * objects by calling +dig+ at each step, returning +nil+ if any - * intermediate step is +nil+. - * - * a = [[1, [2, 3]]] - * - * a.dig(0, 1, 1) #=> 3 - * a.dig(1, 2, 3) #=> nil - * a.dig(0, 0, 0) #=> TypeError: Integer does not have #dig method - * [42, {foo: :bar}].dig(1, :foo) #=> :bar + * call-seq: + * array.dig(index, *identifiers) -> object + * + * Finds and returns the object in nested objects + * that is specified by +index+ and +identifiers+. + * The nested objects may be instances of various classes. + * See {Dig Methods}[rdoc-ref:dig_methods.rdoc]. + * + * Examples: + * a = [:foo, [:bar, :baz, [:bat, :bam]]] + * a.dig(1) # => [:bar, :baz, [:bat, :bam]] + * a.dig(1, 2) # => [:bat, :bam] + * a.dig(1, 2, 0) # => :bat + * a.dig(1, 2, 3) # => nil */ static VALUE @@ -6505,13 +7656,7 @@ finish_exact_sum(long n, VALUE r, VALUE v, int z) if (n != 0) v = rb_fix_plus(LONG2FIX(n), v); if (r != Qundef) { - /* r can be an Integer when mathn is loaded */ - if (FIXNUM_P(r)) - v = rb_fix_plus(r, v); - else if (RB_TYPE_P(r, T_BIGNUM)) - v = rb_big_plus(r, v); - else - v = rb_rational_plus(r, v); + v = rb_rational_plus(r, v); } else if (!n && z) { v = rb_fix_plus(LONG2FIX(0), v); @@ -6521,44 +7666,35 @@ finish_exact_sum(long n, VALUE r, VALUE v, int z) /* * call-seq: - * ary.sum(init=0) -> number - * ary.sum(init=0) {|e| expr } -> number - * - * Returns the sum of elements. - * For example, [e1, e2, e3].sum returns init + e1 + e2 + e3. - * - * If a block is given, the block is applied to each element - * before addition. - * - * If <i>ary</i> is empty, it returns <i>init</i>. - * - * [].sum #=> 0 - * [].sum(0.0) #=> 0.0 - * [1, 2, 3].sum #=> 6 - * [3, 5.5].sum #=> 8.5 - * [2.5, 3.0].sum(0.0) {|e| e * e } #=> 15.25 - * [Object.new].sum #=> TypeError - * - * The (arithmetic) mean value of an array can be obtained as follows. - * - * mean = ary.sum(0.0) / ary.length - * - * This method can be used for non-numeric objects by - * explicit <i>init</i> argument. - * - * ["a", "b", "c"].sum("") #=> "abc" - * [[1], [[2]], [3]].sum([]) #=> [1, [2], 3] - * - * However, Array#join and Array#flatten is faster than Array#sum for - * array of strings and array of arrays. - * - * ["a", "b", "c"].join #=> "abc" - * [[1], [[2]], [3]].flatten(1) #=> [1, [2], 3] - * - * - * Array#sum method may not respect method redefinition of "+" methods - * such as Integer#+. - * + * array.sum(init = 0) -> object + * array.sum(init = 0) {|element| ... } -> object + * + * When no block is given, returns the object equivalent to: + * sum = init + * array.each {|element| sum += element } + * sum + * For example, <tt>[e1, e2, e3].sum</tt> returns <tt>init + e1 + e2 + e3</tt>. + * + * Examples: + * a = [0, 1, 2, 3] + * a.sum # => 6 + * a.sum(100) # => 106 + * + * The elements need not be numeric, but must be <tt>+</tt>-compatible + * with each other and with +init+: + * a = ['abc', 'def', 'ghi'] + * a.sum('jkl') # => "jklabcdefghi" + * + * When a block is given, it is called with each element + * and the block's return value (instead of the element itself) is used as the addend: + * a = ['zero', 1, :two] + * s = a.sum('Coerced and concatenated: ') {|element| element.to_s } + * s # => "Coerced and concatenated: zero1two" + * + * Notes: + * - Array#join and Array#flatten may be faster than Array#sum + * for an \Array of Strings or an \Array of Arrays. + * - Array#sum method may not respect method redefinition of "+" methods such as Integer#+. */ static VALUE @@ -6588,7 +7724,7 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary) n = 0; } } - else if (RB_TYPE_P(e, T_BIGNUM)) + else if (RB_BIGNUM_TYPE_P(e)) v = rb_big_plus(e, v); else if (RB_TYPE_P(e, T_RATIONAL)) { if (r == Qundef) @@ -6608,7 +7744,7 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary) if (RB_FLOAT_TYPE_P(e)) { /* * Kahan-Babuska balancing compensated summation algorithm - * See http://link.springer.com/article/10.1007/s00607-005-0139-x + * See https://link.springer.com/article/10.1007/s00607-005-0139-x */ double f, c; double x, t; @@ -6625,7 +7761,7 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary) x = RFLOAT_VALUE(e); else if (FIXNUM_P(e)) x = FIX2LONG(e); - else if (RB_TYPE_P(e, T_BIGNUM)) + else if (RB_BIGNUM_TYPE_P(e)) x = rb_big2dbl(e); else if (RB_TYPE_P(e, T_RATIONAL)) x = rb_num2dbl(e); @@ -6678,22 +7814,49 @@ rb_ary_deconstruct(VALUE ary) } /* - * Arrays are ordered, integer-indexed collections of any object. + * An \Array is an ordered, integer-indexed collection of objects, + * called _elements_. Any object may be an \Array element. + * + * == \Array Indexes * - * Array indexing starts at 0, as in C or Java. A negative index is assumed - * to be relative to the end of the array---that is, an index of -1 indicates - * the last element of the array, -2 is the next to last element in the - * array, and so on. + * \Array indexing starts at 0, as in C or Java. + * + * A positive index is an offset from the first element: + * - Index 0 indicates the first element. + * - Index 1 indicates the second element. + * - ... + * + * A negative index is an offset, backwards, from the end of the array: + * - Index -1 indicates the last element. + * - Index -2 indicates the next-to-last element. + * - ... + * + * A non-negative index is <i>in range</i> if it is smaller than + * the size of the array. For a 3-element array: + * - Indexes 0 through 2 are in range. + * - Index 3 is out of range. + * + * A negative index is <i>in range</i> if its absolute value is + * not larger than the size of the array. For a 3-element array: + * - Indexes -1 through -3 are in range. + * - Index -4 is out of range. * * == Creating Arrays * - * A new array can be created by using the literal constructor - * <code>[]</code>. Arrays can contain different types of objects. For + * You can create an \Array object explicitly with: + * + * - An {array literal}[doc/syntax/literals_rdoc.html#label-Array+Literals]. + * + * You can convert certain objects to Arrays with: + * + * - \Method {Array}[Kernel.html#method-i-Array]. + * + * An \Array can contain different types of objects. For * example, the array below contains an Integer, a String and a Float: * * ary = [1, "two", 3.0] #=> [1, "two", 3.0] * - * An array can also be created by explicitly calling Array.new with zero, one + * An array can also be created by calling Array.new with zero, one * (the initial size of the Array) or two arguments (the initial size and a * default object). * @@ -6913,14 +8076,190 @@ rb_ary_deconstruct(VALUE ary) * arr.keep_if {|a| a < 4} #=> [1, 2, 3] * arr #=> [1, 2, 3] * + * == What's Here + * + * First, what's elsewhere. \Class \Array: + * + * - Inherits from {class Object}[Object.html#class-Object-label-What-27s+Here]. + * - Includes {module Enumerable}[Enumerable.html#module-Enumerable-label-What-27s+Here], + * which provides dozens of additional methods. + * + * Here, class \Array provides methods that are useful for: + * + * - {Creating an Array}[#class-Array-label-Methods+for+Creating+an+Array] + * - {Querying}[#class-Array-label-Methods+for+Querying] + * - {Comparing}[#class-Array-label-Methods+for+Comparing] + * - {Fetching}[#class-Array-label-Methods+for+Fetching] + * - {Assigning}[#class-Array-label-Methods+for+Assigning] + * - {Deleting}[#class-Array-label-Methods+for+Deleting] + * - {Combining}[#class-Array-label-Methods+for+Combining] + * - {Iterating}[#class-Array-label-Methods+for+Iterating] + * - {Converting}[#class-Array-label-Methods+for+Converting] + * - {And more....}[#class-Array-label-Other+Methods] + * + * === Methods for Creating an Array + * + * ::[]:: Returns a new array populated with given objects. + * ::new:: Returns a new array. + * ::try_convert:: Returns a new array created from a given object. + * + * === Methods for Querying + * + * #length, #size:: Returns the count of elements. + * #include?:: Returns whether any element <tt>==</tt> a given object. + * #empty?:: Returns whether there are no elements. + * #all?:: Returns whether all elements meet a given criterion. + * #any?:: Returns whether any element meets a given criterion. + * #none?:: Returns whether no element <tt>==</tt> a given object. + * #one?:: Returns whether exactly one element <tt>==</tt> a given object. + * #count:: Returns the count of elements that meet a given criterion. + * #find_index, #index:: Returns the index of the first element that meets a given criterion. + * #rindex:: Returns the index of the last element that meets a given criterion. + * #hash:: Returns the integer hash code. + * + * === Methods for Comparing + * {#<=>}[#method-i-3C-3D-3E]:: Returns -1, 0, or 1 + * as +self+ is less than, equal to, or greater than a given object. + * {#==}[#method-i-3D-3D]:: Returns whether each element in +self+ is <tt>==</tt> to the + * corresponding element in a given object. + * #eql?:: Returns whether each element in +self+ is <tt>eql?</tt> to the corresponding + * element in a given object. + + * === Methods for Fetching + * + * These methods do not modify +self+. + * + * #[]:: Returns one or more elements. + * #fetch:: Returns the element at a given offset. + * #first:: Returns one or more leading elements. + * #last:: Returns one or more trailing elements. + * #max:: Returns one or more maximum-valued elements, + * as determined by <tt><=></tt> or a given block. + * #max:: Returns one or more minimum-valued elements, + * as determined by <tt><=></tt> or a given block. + * #minmax:: Returns the minimum-valued and maximum-valued elements, + * as determined by <tt><=></tt> or a given block. + * #assoc:: Returns the first element that is an array + * whose first element <tt>==</tt> a given object. + * #rassoc:: Returns the first element that is an array + * whose second element <tt>==</tt> a given object. + * #at:: Returns the element at a given offset. + * #values_at:: Returns the elements at given offsets. + * #dig:: Returns the object in nested objects + * that is specified by a given index and additional arguments. + * #drop:: Returns trailing elements as determined by a given index. + * #take:: Returns leading elements as determined by a given index. + * #drop_while:: Returns trailing elements as determined by a given block. + * #take_while:: Returns leading elements as determined by a given block. + * #slice:: Returns consecutive elements as determined by a given argument. + * #sort:: Returns all elements in an order determined by <tt><=></tt> or a given block. + * #reverse:: Returns all elements in reverse order. + * #compact:: Returns an array containing all non-+nil+ elements. + * #select, #filter:: Returns an array containing elements selected by a given block. + * #uniq:: Returns an array containing non-duplicate elements. + * #rotate:: Returns all elements with some rotated from one end to the other. + * #bsearch:: Returns an element selected via a binary search + * as determined by a given block. + * #bsearch_index:: Returns the index of an element selected via a binary search + * as determined by a given block. + * #sample:: Returns one or more random elements. + * #shuffle:: Returns elements in a random order. + * + * === Methods for Assigning + * + * These methods add, replace, or reorder elements in +self+. + * + * #[]=:: Assigns specified elements with a given object. + * #push, #append, #<<:: Appends trailing elements. + * #unshift, #prepend:: Prepends leading elements. + * #insert:: Inserts given objects at a given offset; does not replace elements. + * #concat:: Appends all elements from given arrays. + * #fill:: Replaces specified elements with specified objects. + * #replace:: Replaces the content of +self+ with the content of a given array. + * #reverse!:: Replaces +self+ with its elements reversed. + * #rotate!:: Replaces +self+ with its elements rotated. + * #shuffle!:: Replaces +self+ with its elements in random order. + * #sort!:: Replaces +self+ with its elements sorted, + * as determined by <tt><=></tt> or a given block. + * #sort_by!:: Replaces +self+ with its elements sorted, as determined by a given block. + * + * === Methods for Deleting + * + * Each of these methods removes elements from +self+: + * + * #pop:: Removes and returns the last element. + * #shift:: Removes and returns the first element. + * #compact!:: Removes all non-+nil+ elements. + * #delete:: Removes elements equal to a given object. + * #delete_at:: Removes the element at a given offset. + * #delete_if:: Removes elements specified by a given block. + * #keep_if:: Removes elements not specified by a given block. + * #reject!:: Removes elements specified by a given block. + * #select!, #filter!:: Removes elements not specified by a given block. + * #slice!:: Removes and returns a sequence of elements. + * #uniq!:: Removes duplicates. + * + * === Methods for Combining + * + * {#&}[#method-i-26]:: Returns an array containing elements found both in +self+ and a given array. + * #intersection:: Returns an array containing elements found both in +self+ + * and in each given array. + * #+:: Returns an array containing all elements of +self+ followed by all elements of a given array. + * #-:: Returns an array containiing all elements of +self+ that are not found in a given array. + * {#|}[#method-i-7C]:: Returns an array containing all elements of +self+ and all elements of a given array, + * duplicates removed. + * #union:: Returns an array containing all elements of +self+ and all elements of given arrays, + * duplicates removed. + * #difference:: Returns an array containing all elements of +self+ that are not found + * in any of the given arrays.. + * #product:: Returns or yields all combinations of elements from +self+ and given arrays. + * + * === Methods for Iterating + * + * #each:: Passes each element to a given block. + * #reverse_each:: Passes each element, in reverse order, to a given block. + * #each_index:: Passes each element index to a given block. + * #cycle:: Calls a given block with each element, then does so again, + * for a specified number of times, or forever. + * #combination:: Calls a given block with combinations of elements of +self+; + * a combination does not use the same element more than once. + * #permutation:: Calls a given block with permutations of elements of +self+; + * a permutation does not use the same element more than once. + * #repeated_combination:: Calls a given block with combinations of elements of +self+; + * a combination may use the same element more than once. + * #repeated_permutation:: Calls a given block with permutations of elements of +self+; + * a permutation may use the same element more than once. + * + * === Methods for Converting + * + * #map, #collect:: Returns an array containing the block return-value for each element. + * #map!, #collect!:: Replaces each element with a block return-value. + * #flatten:: Returns an array that is a recursive flattening of +self+. + * #flatten!:: Replaces each nested array in +self+ with the elements from that array. + * #inspect, #to_s:: Returns a new String containing the elements. + * #join:: Returns a newsString containing the elements joined by the field separator. + * #to_a:: Returns +self+ or a new array containing all elements. + * #to_ary:: Returns +self+. + * #to_h:: Returns a new hash formed from the elements. + * #transpose:: Transposes +self+, which must be an array of arrays. + * #zip:: Returns a new array of arrays containing +self+ and given arrays; + * follow the link for details. + * + * === Other Methods + * + * #*:: Returns one of the following: + * - With integer argument +n+, a new array that is the concatenation + * of +n+ copies of +self+. + * - With string argument +field_separator+, a new string that is equivalent to + * <tt>join(field_separator)</tt>. + * #abbrev:: Returns a hash of unambiguous abbreviations for elements. + * #pack:: Packs the elements into a binary sequence. + * #sum:: Returns a sum of elements according to either <tt>+</tt> or a given block. */ void Init_Array(void) { -#undef rb_intern -#define rb_intern(str) rb_intern_const(str) - rb_cArray = rb_define_class("Array", rb_cObject); rb_include_module(rb_cArray, rb_mEnumerable); @@ -6950,6 +8289,7 @@ Init_Array(void) rb_define_method(rb_cArray, "union", rb_ary_union_multi, -1); rb_define_method(rb_cArray, "difference", rb_ary_difference_multi, -1); rb_define_method(rb_cArray, "intersection", rb_ary_intersection_multi, -1); + rb_define_method(rb_cArray, "intersect?", rb_ary_intersect_p, 1); rb_define_method(rb_cArray, "<<", rb_ary_push, 1); rb_define_method(rb_cArray, "push", rb_ary_push_m, -1); rb_define_alias(rb_cArray, "append", "push"); @@ -6962,7 +8302,7 @@ Init_Array(void) rb_define_method(rb_cArray, "each_index", rb_ary_each_index, 0); rb_define_method(rb_cArray, "reverse_each", rb_ary_reverse_each, 0); rb_define_method(rb_cArray, "length", rb_ary_length, 0); - rb_define_alias(rb_cArray, "size", "length"); + rb_define_method(rb_cArray, "size", rb_ary_length, 0); rb_define_method(rb_cArray, "empty?", rb_ary_empty_p, 0); rb_define_method(rb_cArray, "find_index", rb_ary_index, -1); rb_define_method(rb_cArray, "index", rb_ary_index, -1); @@ -7022,9 +8362,6 @@ Init_Array(void) 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, -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); rb_define_method(rb_cArray, "combination", rb_ary_combination, 1); @@ -7046,6 +8383,6 @@ Init_Array(void) rb_define_method(rb_cArray, "sum", rb_ary_sum, -1); rb_define_method(rb_cArray, "deconstruct", rb_ary_deconstruct, 0); - - id_random = rb_intern("random"); } + +#include "array.rbinc" |
