From: Yusuke ENDOH Date: 2009-09-10T01:48:38+09:00 Subject: [ruby-dev:39277] Why doesn't Array#product return Enumerator? 遠藤です。 Array#product は Enumerator でなく配列を返しますが、何か理由が あってのことでしょうか。 Array#permutation や combination のように Enumerator を返した方が 自然かつ便利だと思います。これらのメソッドは brute force 的な探索に よく使いますが、product だけ探索前に巨大な配列を確保してしまうので いやらしいです。 仕様変更になってしまいますが、一応言ってみました。どうでしょうか。 Index: array.c =================================================================== --- array.c (revision 24823) +++ array.c (working copy) @@ -3858,30 +3858,35 @@ * 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 + * When invoked with a block, yields all combinations of elements + * from all 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([]) # => [] + * When invoked without a block, returns an enumerator object instead. + * + * [1,2,3].product([4,5]).to_a # => [[1,4],[1,5],[2,4],[2,5],[3,4],[3,5]] + * [1,2].product([1,2]).to_a # => [[1,1],[1,2],[2,1],[2,2]] + * [1,2].product([3,4],[5,6]).to_a # => [[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().to_a # => [[1],[2]] + * [1,2].product([]).to_a # => [] */ static VALUE rb_ary_product(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_PTR(t0); /* The arrays we're computing the product of */ - int *counters = (int*)RSTRING_PTR(t1); /* The current position in each one */ - VALUE result; /* The array we'll be returning */ + volatile VALUE t0, t1; + VALUE *arrays; + int *counters; long i,j; long resultlen = 1; + RETURN_ENUMERATOR(ary, argc, argv); + t0 = tmpbuf(n, sizeof(VALUE)); + t1 = tmpbuf(n, sizeof(int)); + arrays = (VALUE*)RSTRING_PTR(t0); /* The arrays we're computing the product of */ + counters = (int*)RSTRING_PTR(t1); /* The current position in each one */ + RBASIC(t0)->klass = 0; RBASIC(t1)->klass = 0; @@ -3903,7 +3908,6 @@ } /* 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 */ @@ -3913,7 +3917,7 @@ } /* put it on the result array */ - rb_ary_push(result, subarray); + rb_yield(subarray); /* * Increment the last counter. If it overflows, reset to 0 @@ -3930,7 +3934,7 @@ tmpbuf_discard(t0); tmpbuf_discard(t1); - return result; + return ary; } /* Index: test/ruby/test_array.rb =================================================================== --- test/ruby/test_array.rb (revision 24823) +++ test/ruby/test_array.rb (working copy) @@ -1306,14 +1306,14 @@ def test_product assert_equal(@cls[[1,4],[1,5],[2,4],[2,5],[3,4],[3,5]], - @cls[1,2,3].product([4,5])) - assert_equal(@cls[[1,1],[1,2],[2,1],[2,2]], @cls[1,2].product([1,2])) + @cls[1,2,3].product([4,5]).to_a) + assert_equal(@cls[[1,1],[1,2],[2,1],[2,2]], @cls[1,2].product([1,2]).to_a) assert_equal(@cls[[1,3,5],[1,3,6],[1,4,5],[1,4,6], [2,3,5],[2,3,6],[2,4,5],[2,4,6]], - @cls[1,2].product([3,4],[5,6])) - assert_equal(@cls[[1],[2]], @cls[1,2].product) - assert_equal(@cls[], @cls[1,2].product([])) + @cls[1,2].product([3,4],[5,6]).to_a) + assert_equal(@cls[[1],[2]], @cls[1,2].product.to_a) + assert_equal(@cls[], @cls[1,2].product([]).to_a) end def test_permutation -- Yusuke ENDOH