mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* array.c (rb_ary_flatten, rb_ary_flatten_bang): Take an optional
argument that determines the level of recursion to flatten; backported from 1.9. * array.c (rb_ary_shuffle_bang, rb_ary_shuffle, rb_ary_choice, rb_ary_cycle, rb_ary_permutation, rb_ary_combination, rb_ary_product, rb_ary_take, rb_ary_take_while, rb_ary_drop, rb_ary_drop_while): New methods: Array#shuffle, #shuffle!, #choice, #cycle, #permutation, #combination, #product, #take, #take_while, #drop, #drop_while; backported from 1.9. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8@16016 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
7a581f00ad
commit
f2dc726691
4 changed files with 1674 additions and 82 deletions
13
ChangeLog
13
ChangeLog
|
@ -1,3 +1,16 @@
|
|||
Mon Apr 14 19:54:21 2008 Akinori MUSHA <knu@iDaemons.org>
|
||||
|
||||
* array.c (rb_ary_flatten, rb_ary_flatten_bang): Take an optional
|
||||
argument that determines the level of recursion to flatten;
|
||||
backported from 1.9.
|
||||
|
||||
* array.c (rb_ary_shuffle_bang, rb_ary_shuffle, rb_ary_choice,
|
||||
rb_ary_cycle, rb_ary_permutation, rb_ary_combination,
|
||||
rb_ary_product, rb_ary_take, rb_ary_take_while, rb_ary_drop,
|
||||
rb_ary_drop_while): New methods: Array#shuffle, #shuffle!,
|
||||
#choice, #cycle, #permutation, #combination, #product, #take,
|
||||
#take_while, #drop, #drop_while; backported from 1.9.
|
||||
|
||||
Mon Apr 14 19:52:35 2008 Akinori MUSHA <knu@iDaemons.org>
|
||||
|
||||
* ruby.h: New macro: RB_GC_GUARD().
|
||||
|
|
20
NEWS
20
NEWS
|
@ -21,6 +21,12 @@ with all sufficient information, see the ChangeLog file.
|
|||
determine if each element should be counted instead of checking if
|
||||
the element is non-nil.
|
||||
|
||||
* Array#flatten
|
||||
* Array#flatten!
|
||||
|
||||
Takes an optional argument that determines the level of recursion
|
||||
to flatten.
|
||||
|
||||
* Array#index
|
||||
* Array#rindex
|
||||
|
||||
|
@ -43,6 +49,20 @@ with all sufficient information, see the ChangeLog file.
|
|||
Take an optional argument specifying the number of elements to
|
||||
remove.
|
||||
|
||||
* Array#choice
|
||||
* Array#combination
|
||||
* Array#cycle
|
||||
* Array#drop
|
||||
* Array#drop_while
|
||||
* Array#permutation
|
||||
* Array#product
|
||||
* Array#shuffle
|
||||
* Array#shuffle!
|
||||
* Array#take,
|
||||
* Array#take_while
|
||||
|
||||
New methods.
|
||||
|
||||
* Dir#each
|
||||
* Dir#foreach
|
||||
|
||||
|
|
672
array.c
672
array.c
|
@ -960,16 +960,16 @@ rb_ary_index(argc, argv, ary)
|
|||
|
||||
if (argc == 0) {
|
||||
RETURN_ENUMERATOR(ary, 0, 0);
|
||||
for (i=0; i<RARRAY_LEN(ary); i++) {
|
||||
if (RTEST(rb_yield(RARRAY_PTR(ary)[i]))) {
|
||||
for (i=0; i<RARRAY(ary)->len; i++) {
|
||||
if (RTEST(rb_yield(RARRAY(ary)->ptr[i]))) {
|
||||
return LONG2NUM(i);
|
||||
}
|
||||
}
|
||||
return Qnil;
|
||||
}
|
||||
rb_scan_args(argc, argv, "01", &val);
|
||||
for (i=0; i<RARRAY_LEN(ary); i++) {
|
||||
if (rb_equal(RARRAY_PTR(ary)[i], val))
|
||||
for (i=0; i<RARRAY(ary)->len; i++) {
|
||||
if (rb_equal(RARRAY(ary)->ptr[i], val))
|
||||
return LONG2NUM(i);
|
||||
}
|
||||
return Qnil;
|
||||
|
@ -997,25 +997,25 @@ rb_ary_rindex(argc, argv, ary)
|
|||
VALUE ary;
|
||||
{
|
||||
VALUE val;
|
||||
long i = RARRAY_LEN(ary);
|
||||
long i = RARRAY(ary)->len;
|
||||
|
||||
if (argc == 0) {
|
||||
RETURN_ENUMERATOR(ary, 0, 0);
|
||||
while (i--) {
|
||||
if (RTEST(rb_yield(RARRAY_PTR(ary)[i])))
|
||||
if (RTEST(rb_yield(RARRAY(ary)->ptr[i])))
|
||||
return LONG2NUM(i);
|
||||
if (i > RARRAY_LEN(ary)) {
|
||||
i = RARRAY_LEN(ary);
|
||||
if (i > RARRAY(ary)->len) {
|
||||
i = RARRAY(ary)->len;
|
||||
}
|
||||
}
|
||||
return Qnil;
|
||||
}
|
||||
rb_scan_args(argc, argv, "01", &val);
|
||||
while (i--) {
|
||||
if (rb_equal(RARRAY_PTR(ary)[i], val))
|
||||
if (rb_equal(RARRAY(ary)->ptr[i], val))
|
||||
return LONG2NUM(i);
|
||||
if (i > RARRAY_LEN(ary)) {
|
||||
i = RARRAY_LEN(ary);
|
||||
if (i > RARRAY(ary)->len) {
|
||||
i = RARRAY(ary)->len;
|
||||
}
|
||||
}
|
||||
return Qnil;
|
||||
|
@ -2094,7 +2094,7 @@ rb_ary_slice_bang(argc, argv, ary)
|
|||
}
|
||||
|
||||
if (!FIXNUM_P(arg1)) {
|
||||
switch (rb_range_beg_len(arg1, &pos, &len, RARRAY_LEN(ary), 0)) {
|
||||
switch (rb_range_beg_len(arg1, &pos, &len, RARRAY(ary)->len, 0)) {
|
||||
case Qtrue:
|
||||
/* valid range */
|
||||
goto delete_pos_len;
|
||||
|
@ -2556,9 +2556,9 @@ rb_ary_assoc(ary, key)
|
|||
VALUE v;
|
||||
|
||||
for (i = 0; i < RARRAY(ary)->len; ++i) {
|
||||
v = rb_check_array_type(RARRAY_PTR(ary)[i]);
|
||||
v = rb_check_array_type(RARRAY(ary)->ptr[i]);
|
||||
if (!NIL_P(v) && RARRAY(v)->len > 0 &&
|
||||
rb_equal(RARRAY_PTR(v)[0], key))
|
||||
rb_equal(RARRAY(v)->ptr[0], key))
|
||||
return v;
|
||||
}
|
||||
return Qnil;
|
||||
|
@ -3002,14 +3002,14 @@ rb_ary_nitems(ary)
|
|||
if (rb_block_given_p()) {
|
||||
long i;
|
||||
|
||||
for (i=0; i<RARRAY_LEN(ary); i++) {
|
||||
VALUE v = RARRAY_PTR(ary)[i];
|
||||
for (i=0; i<RARRAY(ary)->len; i++) {
|
||||
VALUE v = RARRAY(ary)->ptr[i];
|
||||
if (RTEST(rb_yield(v))) n++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
VALUE *p = RARRAY_PTR(ary);
|
||||
VALUE *pend = p + RARRAY_LEN(ary);
|
||||
VALUE *p = RARRAY(ary)->ptr;
|
||||
VALUE *pend = p + RARRAY(ary)->len;
|
||||
|
||||
while (p < pend) {
|
||||
if (!NIL_P(*p)) n++;
|
||||
|
@ -3019,101 +3019,631 @@ rb_ary_nitems(ary)
|
|||
return LONG2NUM(n);
|
||||
}
|
||||
|
||||
static long
|
||||
flatten(ary, idx, ary2, memo)
|
||||
static VALUE
|
||||
flatten(ary, level, modified)
|
||||
VALUE ary;
|
||||
long idx;
|
||||
VALUE ary2, memo;
|
||||
int level;
|
||||
int *modified;
|
||||
{
|
||||
VALUE id;
|
||||
long i = idx;
|
||||
long n, lim = idx + RARRAY(ary2)->len;
|
||||
long i = 0;
|
||||
VALUE stack, result, tmp, elt;
|
||||
st_table *memo;
|
||||
st_data_t id;
|
||||
|
||||
id = rb_obj_id(ary2);
|
||||
if (rb_ary_includes(memo, id)) {
|
||||
rb_raise(rb_eArgError, "tried to flatten recursive array");
|
||||
}
|
||||
rb_ary_push(memo, id);
|
||||
rb_ary_splice(ary, idx, 1, ary2);
|
||||
while (i < lim) {
|
||||
VALUE tmp;
|
||||
stack = rb_ary_new();
|
||||
result = rb_ary_new();
|
||||
memo = st_init_numtable();
|
||||
st_insert(memo, (st_data_t)ary, (st_data_t)Qtrue);
|
||||
*modified = 0;
|
||||
|
||||
tmp = rb_check_array_type(rb_ary_elt(ary, i));
|
||||
if (!NIL_P(tmp)) {
|
||||
n = flatten(ary, i, tmp, memo);
|
||||
i += n; lim += n;
|
||||
while (1) {
|
||||
while (i < RARRAY(ary)->len) {
|
||||
elt = RARRAY(ary)->ptr[i++];
|
||||
tmp = rb_check_array_type(elt);
|
||||
if (NIL_P(tmp) || (level >= 0 && RARRAY(stack)->len / 2 >= level)) {
|
||||
rb_ary_push(result, elt);
|
||||
}
|
||||
else {
|
||||
*modified = 1;
|
||||
id = (st_data_t)tmp;
|
||||
if (st_lookup(memo, id, 0)) {
|
||||
rb_raise(rb_eArgError, "tried to flatten recursive array");
|
||||
}
|
||||
st_insert(memo, id, (st_data_t)Qtrue);
|
||||
rb_ary_push(stack, ary);
|
||||
rb_ary_push(stack, LONG2NUM(i));
|
||||
ary = tmp;
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
if (RARRAY(stack)->len == 0) {
|
||||
break;
|
||||
}
|
||||
id = (st_data_t)ary;
|
||||
st_delete(memo, &id, 0);
|
||||
tmp = rb_ary_pop(stack);
|
||||
i = NUM2LONG(tmp);
|
||||
ary = rb_ary_pop(stack);
|
||||
}
|
||||
rb_ary_pop(memo);
|
||||
|
||||
return lim - idx - 1; /* returns number of increased items */
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* array.flatten! -> array or nil
|
||||
* array.flatten!(level) -> array or nil
|
||||
*
|
||||
* Flattens _self_ in place.
|
||||
* Returns <code>nil</code> if no modifications were made (i.e.,
|
||||
* <i>array</i> contains no subarrays.)
|
||||
* <i>array</i> contains no subarrays.) If the optional <i>level</i>
|
||||
* 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]]
|
||||
*/
|
||||
|
||||
static VALUE
|
||||
rb_ary_flatten_bang(ary)
|
||||
rb_ary_flatten_bang(argc, argv, ary)
|
||||
int argc;
|
||||
VALUE *argv;
|
||||
VALUE ary;
|
||||
{
|
||||
long i = 0;
|
||||
int mod = 0;
|
||||
VALUE memo = Qnil;
|
||||
int mod = 0, level = -1;
|
||||
VALUE result, lv;
|
||||
|
||||
while (i<RARRAY(ary)->len) {
|
||||
VALUE ary2 = RARRAY(ary)->ptr[i];
|
||||
VALUE tmp;
|
||||
rb_scan_args(argc, argv, "01", &lv);
|
||||
if (!NIL_P(lv)) level = NUM2INT(lv);
|
||||
if (level == 0) return ary;
|
||||
|
||||
tmp = rb_check_array_type(ary2);
|
||||
if (!NIL_P(tmp)) {
|
||||
if (NIL_P(memo)) {
|
||||
memo = rb_ary_new();
|
||||
}
|
||||
i += flatten(ary, i, tmp, memo);
|
||||
mod = 1;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
result = flatten(ary, level, &mod);
|
||||
if (mod == 0) return Qnil;
|
||||
rb_ary_replace(ary, result);
|
||||
|
||||
return ary;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* array.flatten -> an_array
|
||||
* array.flatten(level) -> an_array
|
||||
*
|
||||
* Returns a new array that is a one-dimensional flattening of this
|
||||
* array (recursively). That is, for every element that is an array,
|
||||
* extract its elements into the new array.
|
||||
* extract its elements into the new array. If the optional
|
||||
* <i>level</i> 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.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]]
|
||||
*/
|
||||
|
||||
static VALUE
|
||||
rb_ary_flatten(ary)
|
||||
rb_ary_flatten(argc, argv, ary)
|
||||
int argc;
|
||||
VALUE *argv;
|
||||
VALUE ary;
|
||||
{
|
||||
ary = rb_ary_dup(ary);
|
||||
rb_ary_flatten_bang(ary);
|
||||
int mod = 0, level = -1;
|
||||
VALUE result, lv;
|
||||
|
||||
rb_scan_args(argc, argv, "01", &lv);
|
||||
if (!NIL_P(lv)) level = NUM2INT(lv);
|
||||
if (level == 0) return ary;
|
||||
|
||||
result = flatten(ary, level, &mod);
|
||||
if (OBJ_TAINTED(ary)) OBJ_TAINT(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* array.shuffle! -> array or nil
|
||||
*
|
||||
* Shuffles elements in _self_ in place.
|
||||
*/
|
||||
|
||||
|
||||
static VALUE
|
||||
rb_ary_shuffle_bang(ary)
|
||||
VALUE ary;
|
||||
{
|
||||
long i = RARRAY(ary)->len;
|
||||
|
||||
rb_ary_modify(ary);
|
||||
while (i) {
|
||||
long j = rb_genrand_real()*i;
|
||||
VALUE tmp = RARRAY(ary)->ptr[--i];
|
||||
RARRAY(ary)->ptr[i] = RARRAY(ary)->ptr[j];
|
||||
RARRAY(ary)->ptr[j] = tmp;
|
||||
}
|
||||
return ary;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* array.shuffle -> an_array
|
||||
*
|
||||
* Returns a new array with elements of this array shuffled.
|
||||
*
|
||||
* a = [ 1, 2, 3 ] #=> [1, 2, 3]
|
||||
* a.shuffle #=> [2, 3, 1]
|
||||
*/
|
||||
|
||||
static VALUE
|
||||
rb_ary_shuffle(VALUE ary)
|
||||
{
|
||||
ary = rb_ary_dup(ary);
|
||||
rb_ary_shuffle_bang(ary);
|
||||
return ary;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* array.choice -> obj
|
||||
*
|
||||
* Choose a random element from an array.
|
||||
*/
|
||||
|
||||
|
||||
static VALUE
|
||||
rb_ary_choice(ary)
|
||||
VALUE ary;
|
||||
{
|
||||
long i, j;
|
||||
|
||||
i = RARRAY(ary)->len;
|
||||
if (i == 0) return Qnil;
|
||||
j = rb_genrand_real()*i;
|
||||
return RARRAY(ary)->ptr[j];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ary.cycle {|obj| block }
|
||||
* ary.cycle(n) {|obj| block }
|
||||
*
|
||||
* Calls <i>block</i> for each element repeatedly _n_ times or
|
||||
* forever if none or nil is given. If a non-positive number is
|
||||
* given or the array is empty, does nothing. Returns nil if the
|
||||
* loop has finished without getting interrupted.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
static VALUE
|
||||
rb_ary_cycle(argc, argv, ary)
|
||||
int argc;
|
||||
VALUE *argv;
|
||||
VALUE ary;
|
||||
{
|
||||
long n, i;
|
||||
VALUE nv = Qnil;
|
||||
|
||||
rb_scan_args(argc, argv, "01", &nv);
|
||||
|
||||
RETURN_ENUMERATOR(ary, argc, argv);
|
||||
if (NIL_P(nv)) {
|
||||
n = -1;
|
||||
}
|
||||
else {
|
||||
n = NUM2LONG(nv);
|
||||
if (n <= 0) return Qnil;
|
||||
}
|
||||
|
||||
while (RARRAY(ary)->len > 0 && (n < 0 || 0 < n--)) {
|
||||
for (i=0; i<RARRAY(ary)->len; i++) {
|
||||
rb_yield(RARRAY(ary)->ptr[i]);
|
||||
}
|
||||
}
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
#define tmpbuf(n, size) rb_str_tmp_new((n)*(size))
|
||||
|
||||
/*
|
||||
* Recursively compute permutations of r elements of the set [0..n-1].
|
||||
* When we have a complete permutation of array indexes, copy the values
|
||||
* at those indexes into a new array and yield that array.
|
||||
*
|
||||
* n: the size of the set
|
||||
* r: the number of elements in each permutation
|
||||
* p: the array (of size r) that we're filling in
|
||||
* index: what index we're filling in now
|
||||
* used: an array of booleans: whether a given index is already used
|
||||
* values: the Ruby array that holds the actual values to permute
|
||||
*/
|
||||
static void
|
||||
permute0(n, r, p, index, used, values)
|
||||
long n, r, *p, index;
|
||||
int *used;
|
||||
VALUE values;
|
||||
{
|
||||
long i,j;
|
||||
for (i = 0; i < n; i++) {
|
||||
if (used[i] == 0) {
|
||||
p[index] = i;
|
||||
if (index < r-1) { /* if not done yet */
|
||||
used[i] = 1; /* mark index used */
|
||||
permute0(n, r, p, index+1, /* recurse */
|
||||
used, values);
|
||||
used[i] = 0; /* index unused */
|
||||
}
|
||||
else {
|
||||
/* We have a complete permutation of array indexes */
|
||||
/* Build a ruby array of the corresponding values */
|
||||
/* And yield it to the associated block */
|
||||
VALUE result = rb_ary_new2(r);
|
||||
VALUE *result_array = RARRAY(result)->ptr;
|
||||
const VALUE *values_array = RARRAY(values)->ptr;
|
||||
|
||||
for (j = 0; j < r; j++) result_array[j] = values_array[p[j]];
|
||||
RARRAY(result)->len = r;
|
||||
rb_yield(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ary.permutation { |p| block } -> array
|
||||
* ary.permutation -> enumerator
|
||||
* ary.permutation(n) { |p| block } -> array
|
||||
* ary.permutation(n) -> enumerator
|
||||
*
|
||||
* When invoked with a block, yield all permutations of length <i>n</i>
|
||||
* of the elements of <i>ary</i>, then return the array itself.
|
||||
* If <i>n</i> is not specified, yield all permutations of all elements.
|
||||
* The implementation makes no guarantees about the order in which
|
||||
* the permutations are yielded.
|
||||
*
|
||||
* When invoked without a block, return an enumerator object 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
|
||||
*/
|
||||
|
||||
static VALUE
|
||||
rb_ary_permutation(argc, argv, ary)
|
||||
int argc;
|
||||
VALUE *argv;
|
||||
VALUE ary;
|
||||
{
|
||||
VALUE num;
|
||||
long r, n, i;
|
||||
|
||||
n = RARRAY(ary)->len; /* Array length */
|
||||
RETURN_ENUMERATOR(ary, argc, argv); /* Return enumerator if no block */
|
||||
rb_scan_args(argc, argv, "01", &num);
|
||||
r = NIL_P(num) ? n : NUM2LONG(num); /* Permutation size from argument */
|
||||
|
||||
if (r < 0 || n < r) {
|
||||
/* no permutations: yield nothing */
|
||||
}
|
||||
else if (r == 0) { /* exactly one permutation: the zero-length array */
|
||||
rb_yield(rb_ary_new2(0));
|
||||
}
|
||||
else if (r == 1) { /* this is a special, easy case */
|
||||
for (i = 0; i < RARRAY(ary)->len; i++) {
|
||||
rb_yield(rb_ary_new3(1, RARRAY(ary)->ptr[i]));
|
||||
}
|
||||
}
|
||||
else { /* this is the general case */
|
||||
volatile VALUE t0 = tmpbuf(n,sizeof(long));
|
||||
long *p = (long*)RSTRING(t0)->ptr;
|
||||
volatile VALUE t1 = tmpbuf(n,sizeof(int));
|
||||
int *used = (int*)RSTRING(t1)->ptr;
|
||||
VALUE ary0 = ary_make_shared(ary); /* private defensive copy of ary */
|
||||
|
||||
for (i = 0; i < n; i++) used[i] = 0; /* initialize array */
|
||||
|
||||
permute0(n, r, p, 0, used, ary0); /* compute and yield permutations */
|
||||
RB_GC_GUARD(t0);
|
||||
RB_GC_GUARD(t1);
|
||||
}
|
||||
return ary;
|
||||
}
|
||||
|
||||
static long
|
||||
combi_len(n, k)
|
||||
long n, k;
|
||||
{
|
||||
long i, val = 1;
|
||||
|
||||
if (k*2 > n) k = n-k;
|
||||
if (k == 0) return 1;
|
||||
if (k < 0) return 0;
|
||||
val = 1;
|
||||
for (i=1; i <= k; i++,n--) {
|
||||
long m = val;
|
||||
val *= n;
|
||||
if (val < m) {
|
||||
rb_raise(rb_eRangeError, "too big for combination");
|
||||
}
|
||||
val /= i;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ary.combination(n) { |c| block } -> ary
|
||||
* ary.combination(n) -> enumerator
|
||||
*
|
||||
* When invoked with a block, yields all combinations of length <i>n</i>
|
||||
* of elements from <i>ary</i> and then returns <i>ary</i> itself.
|
||||
* The implementation makes no guarantees about the order in which
|
||||
* the combinations are yielded.
|
||||
*
|
||||
* When invoked without a block, returns an enumerator object 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
|
||||
*
|
||||
*/
|
||||
|
||||
static VALUE
|
||||
rb_ary_combination(ary, num)
|
||||
VALUE ary;
|
||||
VALUE num;
|
||||
{
|
||||
long n, i, len;
|
||||
|
||||
n = NUM2LONG(num);
|
||||
RETURN_ENUMERATOR(ary, 1, &num);
|
||||
len = RARRAY(ary)->len;
|
||||
if (n < 0 || len < n) {
|
||||
/* yield nothing */
|
||||
}
|
||||
else if (n == 0) {
|
||||
rb_yield(rb_ary_new2(0));
|
||||
}
|
||||
else if (n == 1) {
|
||||
for (i = 0; i < len; i++) {
|
||||
rb_yield(rb_ary_new3(1, RARRAY(ary)->ptr[i]));
|
||||
}
|
||||
}
|
||||
else {
|
||||
volatile VALUE t0 = tmpbuf(n+1, sizeof(long));
|
||||
long *stack = (long*)RSTRING(t0)->ptr;
|
||||
long nlen = combi_len(len, n);
|
||||
volatile VALUE cc = rb_ary_new2(n);
|
||||
VALUE *chosen = RARRAY(cc)->ptr;
|
||||
long lev = 0;
|
||||
|
||||
RBASIC(cc)->klass = 0;
|
||||
MEMZERO(stack, long, n);
|
||||
stack[0] = -1;
|
||||
for (i = 0; i < nlen; i++) {
|
||||
chosen[lev] = RARRAY(ary)->ptr[stack[lev+1]];
|
||||
for (lev++; lev < n; lev++) {
|
||||
chosen[lev] = RARRAY(ary)->ptr[stack[lev+1] = stack[lev]+1];
|
||||
}
|
||||
rb_yield(rb_ary_new4(n, chosen));
|
||||
do {
|
||||
stack[lev--]++;
|
||||
} while (lev && (stack[lev+1]+n == len+lev+1));
|
||||
}
|
||||
}
|
||||
return ary;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ary.product(other_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 ary and the argument arrays
|
||||
*
|
||||
* [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([]) # => []
|
||||
*/
|
||||
|
||||
static VALUE
|
||||
rb_ary_product(argc, argv, ary)
|
||||
int argc;
|
||||
VALUE *argv;
|
||||
VALUE ary;
|
||||
{
|
||||
int n = argc+1; /* How many arrays we're operating on */
|
||||
volatile VALUE t0 = tmpbuf(n, sizeof(VALUE));
|
||||
volatile VALUE t1 = tmpbuf(n, sizeof(int));
|
||||
VALUE *arrays = (VALUE*)RSTRING(t0)->ptr; /* The arrays we're computing the product of */
|
||||
int *counters = (int*)RSTRING(t1)->ptr; /* The current position in each one */
|
||||
VALUE result; /* The array we'll be returning */
|
||||
long i,j;
|
||||
long resultlen = 1;
|
||||
|
||||
RBASIC(t0)->klass = 0;
|
||||
RBASIC(t1)->klass = 0;
|
||||
|
||||
/* initialize the arrays of arrays */
|
||||
arrays[0] = ary;
|
||||
for (i = 1; i < n; i++) arrays[i] = to_ary(argv[i-1]);
|
||||
|
||||
/* initialize the counters for the arrays */
|
||||
for (i = 0; i < n; i++) counters[i] = 0;
|
||||
|
||||
/* Compute the length of the result array; return [] if any is empty */
|
||||
for (i = 0; i < n; i++) {
|
||||
long k = RARRAY(arrays[i])->len, l = resultlen;
|
||||
if (k == 0) return rb_ary_new2(0);
|
||||
resultlen *= k;
|
||||
if (resultlen < k || resultlen < l || resultlen / k != l) {
|
||||
rb_raise(rb_eRangeError, "too big to product");
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise, allocate and fill in an array of results */
|
||||
result = rb_ary_new2(resultlen);
|
||||
for (i = 0; i < resultlen; i++) {
|
||||
int m;
|
||||
/* fill in one subarray */
|
||||
VALUE subarray = rb_ary_new2(n);
|
||||
for (j = 0; j < n; j++) {
|
||||
rb_ary_push(subarray, rb_ary_entry(arrays[j], counters[j]));
|
||||
}
|
||||
|
||||
/* put it on the result array */
|
||||
rb_ary_push(result, subarray);
|
||||
|
||||
/*
|
||||
* Increment the last counter. If it overflows, reset to 0
|
||||
* and increment the one before it.
|
||||
*/
|
||||
m = n-1;
|
||||
counters[m]++;
|
||||
while (m > 0 && counters[m] == RARRAY(arrays[m])->len) {
|
||||
counters[m] = 0;
|
||||
m--;
|
||||
counters[m]++;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ary.take(n) => array
|
||||
*
|
||||
* Returns first n elements from <i>ary</i>.
|
||||
*
|
||||
* a = [1, 2, 3, 4, 5, 0]
|
||||
* a.take(3) # => [1, 2, 3]
|
||||
*
|
||||
*/
|
||||
|
||||
static VALUE
|
||||
rb_ary_take(obj, n)
|
||||
VALUE obj;
|
||||
VALUE n;
|
||||
{
|
||||
long len = NUM2LONG(n);
|
||||
if (len < 0) {
|
||||
rb_raise(rb_eArgError, "attempt to take negative size");
|
||||
}
|
||||
|
||||
return rb_ary_subseq(obj, 0, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ary.take_while {|arr| block } => array
|
||||
*
|
||||
* Passes elements to the block until the block returns nil or false,
|
||||
* then stops iterating and returns an array of all prior elements.
|
||||
*
|
||||
* a = [1, 2, 3, 4, 5, 0]
|
||||
* a.take_while {|i| i < 3 } # => [1, 2]
|
||||
*
|
||||
*/
|
||||
|
||||
static VALUE
|
||||
rb_ary_take_while(ary)
|
||||
VALUE ary;
|
||||
{
|
||||
VALUE result;
|
||||
long i;
|
||||
|
||||
RETURN_ENUMERATOR(ary, 0, 0);
|
||||
for (i = 0; i < RARRAY(ary)->len; i++) {
|
||||
if (!RTEST(rb_yield(RARRAY(ary)->ptr[i]))) break;
|
||||
}
|
||||
return rb_ary_take(ary, LONG2FIX(i));
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ary.drop(n) => array
|
||||
*
|
||||
* Drops first n elements from <i>ary</i>, and returns rest elements
|
||||
* in an array.
|
||||
*
|
||||
* a = [1, 2, 3, 4, 5, 0]
|
||||
* a.drop(3) # => [4, 5, 0]
|
||||
*
|
||||
*/
|
||||
|
||||
static VALUE
|
||||
rb_ary_drop(ary, n)
|
||||
VALUE ary;
|
||||
VALUE n;
|
||||
{
|
||||
VALUE result;
|
||||
long pos = NUM2LONG(n);
|
||||
if (pos < 0) {
|
||||
rb_raise(rb_eArgError, "attempt to drop negative size");
|
||||
}
|
||||
|
||||
result = rb_ary_subseq(ary, pos, RARRAY(ary)->len);
|
||||
if (result == Qnil) result = rb_ary_new();
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ary.drop_while {|arr| block } => array
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* a = [1, 2, 3, 4, 5, 0]
|
||||
* a.drop_while {|i| i < 3 } # => [3, 4, 5, 0]
|
||||
*
|
||||
*/
|
||||
|
||||
static VALUE
|
||||
rb_ary_drop_while(ary)
|
||||
VALUE ary;
|
||||
{
|
||||
VALUE result;
|
||||
long i;
|
||||
|
||||
RETURN_ENUMERATOR(ary, 0, 0);
|
||||
for (i = 0; i < RARRAY(ary)->len; i++) {
|
||||
if (!RTEST(rb_yield(RARRAY(ary)->ptr[i]))) break;
|
||||
}
|
||||
return rb_ary_drop(ary, LONG2FIX(i));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Arrays are ordered, integer-indexed collections of any object.
|
||||
* 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
|
||||
|
@ -3207,9 +3737,21 @@ Init_Array()
|
|||
rb_define_method(rb_cArray, "uniq!", rb_ary_uniq_bang, 0);
|
||||
rb_define_method(rb_cArray, "compact", rb_ary_compact, 0);
|
||||
rb_define_method(rb_cArray, "compact!", rb_ary_compact_bang, 0);
|
||||
rb_define_method(rb_cArray, "flatten", rb_ary_flatten, 0);
|
||||
rb_define_method(rb_cArray, "flatten!", rb_ary_flatten_bang, 0);
|
||||
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, "nitems", rb_ary_nitems, 0);
|
||||
rb_define_method(rb_cArray, "shuffle!", rb_ary_shuffle_bang, 0);
|
||||
rb_define_method(rb_cArray, "shuffle", rb_ary_shuffle, 0);
|
||||
rb_define_method(rb_cArray, "choice", rb_ary_choice, 0);
|
||||
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);
|
||||
rb_define_method(rb_cArray, "product", rb_ary_product, -1);
|
||||
|
||||
rb_define_method(rb_cArray, "take", rb_ary_take, 1);
|
||||
rb_define_method(rb_cArray, "take_while", rb_ary_take_while, 0);
|
||||
rb_define_method(rb_cArray, "drop", rb_ary_drop, 1);
|
||||
rb_define_method(rb_cArray, "drop_while", rb_ary_drop_while, 0);
|
||||
|
||||
id_cmp = rb_intern("<=>");
|
||||
inspect_key = rb_intern("__inspect_key__");
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue