From bd372844bd9e7d4d96ba770bbfee47e8ffe3dc10 Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Thu, 6 Mar 2014 19:59:15 -0800 Subject: [PATCH 1/4] Enumerable#select to take args and use === --- enum.c | 43 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/enum.c b/enum.c index cd2d49d6d37e63..c7526e423ca435 100644 --- a/enum.c +++ b/enum.c @@ -299,7 +299,7 @@ enum_find_index(int argc, VALUE *argv, VALUE obj) } static VALUE -find_all_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary)) +find_all_iter_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary)) { ENUM_WANT_SVALUE(); @@ -309,6 +309,22 @@ find_all_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary)) return Qnil; } +static VALUE +find_all_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args)) +{ + NODE *memo = RNODE(args); + VALUE ary = memo->u1.value; + long j; + ENUM_WANT_SVALUE(); + + for (j=0; ju2.value, i); + } + } + return Qnil; +} + static VALUE enum_size(VALUE self, VALUE args, VALUE eobj) { @@ -338,14 +354,27 @@ enum_size(VALUE self, VALUE args, VALUE eobj) */ static VALUE -enum_find_all(VALUE obj) +enum_find_all(int argc, VALUE *argv, VALUE obj) { VALUE ary; + NODE *memo; + VALUE items = Qnil; - RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size); + if (argc == 0) { + RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size); - ary = rb_ary_new(); - rb_block_call(obj, id_each, 0, 0, find_all_i, ary); + ary = rb_ary_new(); + rb_block_call(obj, id_each, 0, 0, find_all_iter_i, ary); + } else { + rb_scan_args(argc, argv, "*", &items); + + if (rb_block_given_p()) + rb_warn("given block not used"); + + ary = rb_ary_new(); + memo = NEW_MEMO(items, ary, 0); + rb_block_call(obj, id_each, 0, 0, find_all_i, (VALUE)memo); + } return ary; } @@ -3052,8 +3081,8 @@ Init_Enumerable(void) rb_define_method(rb_mEnumerable, "find", enum_find, -1); rb_define_method(rb_mEnumerable, "detect", enum_find, -1); rb_define_method(rb_mEnumerable, "find_index", enum_find_index, -1); - rb_define_method(rb_mEnumerable, "find_all", enum_find_all, 0); - rb_define_method(rb_mEnumerable, "select", enum_find_all, 0); + rb_define_method(rb_mEnumerable, "find_all", enum_find_all, -1); + rb_define_method(rb_mEnumerable, "select", enum_find_all, -1); rb_define_method(rb_mEnumerable, "reject", enum_reject, 0); rb_define_method(rb_mEnumerable, "collect", enum_collect, 0); rb_define_method(rb_mEnumerable, "map", enum_collect, 0); From 17df51bff9d4de265c703a9fa8f345417478edbf Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Mon, 10 Mar 2014 09:32:50 -0700 Subject: [PATCH 2/4] Array#select to take args and use ===; documentation; tests --- array.c | 44 +++++++++++++++++++++++++++++++---------- enum.c | 14 +++++++++++-- test/ruby/test_array.rb | 3 +++ test/ruby/test_enum.rb | 2 ++ 4 files changed, 51 insertions(+), 12 deletions(-) diff --git a/array.c b/array.c index 5cf995c303ad66..bd1939ed9ef7f8 100644 --- a/array.c +++ b/array.c @@ -2777,13 +2777,21 @@ rb_ary_values_at(int argc, VALUE *argv, VALUE ary) /* * call-seq: * ary.select { |item| block } -> new_ary + * ary.select(selector, ...) -> new_ary * ary.select -> Enumerator * - * Returns a new array containing all elements of +ary+ - * for which the given +block+ returns a true value. + * If a list of selectors is given, returns an array containing all elements + * of +ary+ for which selector === ary for one of the selectors. + * + * If no arguments are given, 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. * + * %w{ foo bar baz quux }.select(/ba/, /f/) #=> ["foo", "bar", "baz"] + * + * [1, "two", 3.14, {}, :five].select(Numeric, Enumerable) #=> [1, 3.14, {}] + * * [1,2,3,4,5].select { |num| num.even? } #=> [2, 4] * * a = %w{ a b c d e f } @@ -2793,18 +2801,34 @@ rb_ary_values_at(int argc, VALUE *argv, VALUE ary) */ static VALUE -rb_ary_select(VALUE ary) +rb_ary_select(int argc, VALUE *argv, VALUE ary) { VALUE result; - long i; + long i, j; - RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length); - result = rb_ary_new2(RARRAY_LEN(ary)); - for (i = 0; i < RARRAY_LEN(ary); i++) { - if (RTEST(rb_yield(RARRAY_AREF(ary, i)))) { - rb_ary_push(result, rb_ary_elt(ary, i)); + if (argc == 0) { + RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length); + result = rb_ary_new2(RARRAY_LEN(ary)); + for (i = 0; i < RARRAY_LEN(ary); i++) { + if (RTEST(rb_yield(RARRAY_AREF(ary, i)))) { + rb_ary_push(result, rb_ary_elt(ary, i)); + } + } + } else { + if (rb_block_given_p()) + rb_warn("given block not used"); + + result = rb_ary_new2(RARRAY_LEN(ary)); + for (i = 0; i < RARRAY_LEN(ary); i++) { + for (j = 0; j < argc; j++) { + if (RTEST(rb_funcall(argv[j], idEqq, 1, RARRAY_AREF(ary, i)))) { + rb_ary_push(result, rb_ary_elt(ary, i)); + break; + } + } } } + return result; } @@ -5666,7 +5690,7 @@ Init_Array(void) rb_define_method(rb_cArray, "collect!", rb_ary_collect_bang, 0); rb_define_method(rb_cArray, "map", rb_ary_collect, 0); rb_define_method(rb_cArray, "map!", rb_ary_collect_bang, 0); - rb_define_method(rb_cArray, "select", rb_ary_select, 0); + rb_define_method(rb_cArray, "select", rb_ary_select, -1); rb_define_method(rb_cArray, "select!", rb_ary_select_bang, 0); rb_define_method(rb_cArray, "keep_if", rb_ary_keep_if, 0); rb_define_method(rb_cArray, "values_at", rb_ary_values_at, -1); diff --git a/enum.c b/enum.c index c7526e423ca435..ede7ce95e58e84 100644 --- a/enum.c +++ b/enum.c @@ -320,6 +320,7 @@ find_all_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args)) for (j=0; ju2.value, i); + break; } } return Qnil; @@ -337,15 +338,24 @@ enum_size(VALUE self, VALUE args, VALUE eobj) * call-seq: * enum.find_all { |obj| block } -> array * enum.select { |obj| block } -> array + * enum.find_all(selector, ...) -> array + * enum.select(selector, ...) -> array * enum.find_all -> an_enumerator * enum.select -> an_enumerator * - * Returns an array containing all elements of +enum+ - * for which the given +block+ returns a true value. + * If a list of selectors is given, returns an array containing all elements + * of +enum+ for which selector === enum for one of the + * selectors. + * + * If no arguments are given, returns an array containing all elements of + * +enum+ for which the given +block+ returns a true value. * * If no block is given, an Enumerator is returned instead. * * + * ("aaa".."zzz").select(/ru[a-e]/, /[ie]rb/) + * #=> ["erb", "irb", "rua", "rub", "ruc", "rud", "rue"] + * * (1..10).find_all { |i| i % 3 == 0 } #=> [3, 6, 9] * * [1,2,3,4,5].select { |num| num.even? } #=> [2, 4] diff --git a/test/ruby/test_array.rb b/test/ruby/test_array.rb index d8ee3abb2855c3..abaa9d67f17634 100644 --- a/test/ruby/test_array.rb +++ b/test/ruby/test_array.rb @@ -1983,6 +1983,9 @@ def test_values_at2 def test_select assert_equal([0, 2], [0, 1, 2, 3].select {|x| x % 2 == 0 }) + assert_equal(["foo", "bar", "baz"], + ["foo", "bar", "baz", "quux"].select(/ba/, /f/)) + assert_equal([1, 3.1, {}], [1, "two", 3.1, {}, :bar].select(Numeric, Enumerable)) end # also keep_if diff --git a/test/ruby/test_enum.rb b/test/ruby/test_enum.rb index beddb817cd14a9..725482c508495b 100644 --- a/test/ruby/test_enum.rb +++ b/test/ruby/test_enum.rb @@ -93,6 +93,8 @@ def test_find_index def test_find_all assert_equal([1, 3, 1], @obj.find_all {|x| x % 2 == 1 }) + assert_equal([1, 2, 3, 1, 2], @obj.find_all(Numeric)) + assert_equal([2, 3, 2], @obj.find_all(3, 2)) end def test_reject From 2f9697e463dbf8bf574531433a86e7dc355c3cb3 Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Tue, 1 Apr 2014 20:01:41 -0700 Subject: [PATCH 3/4] Enumerable#reject to take args and use ===; documentation; tests --- enum.c | 57 +++++++++++++++++++++++++++++++++++------- test/ruby/test_enum.rb | 3 ++- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/enum.c b/enum.c index ede7ce95e58e84..b6d3eb93bca575 100644 --- a/enum.c +++ b/enum.c @@ -375,7 +375,8 @@ enum_find_all(int argc, VALUE *argv, VALUE obj) ary = rb_ary_new(); rb_block_call(obj, id_each, 0, 0, find_all_iter_i, ary); - } else { + } + else { rb_scan_args(argc, argv, "*", &items); if (rb_block_given_p()) @@ -390,7 +391,7 @@ enum_find_all(int argc, VALUE *argv, VALUE obj) } static VALUE -reject_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary)) +reject_iter_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary)) { ENUM_WANT_SVALUE(); @@ -400,16 +401,41 @@ reject_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary)) return Qnil; } +static VALUE +reject_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args)) +{ + NODE *memo = RNODE(args); + VALUE ary = memo->u1.value; + long j; + ENUM_WANT_SVALUE(); + + for (j=0; ju2.value, i); + return Qnil; +} + /* * call-seq: * enum.reject { |obj| block } -> array + * enum.reject(selector, ...) -> array * enum.reject -> an_enumerator * - * Returns an array for all elements of +enum+ for which the given - * +block+ returns false. + * If a list of selectors is given, returns an array containing all elements + * of +enum+ for which selector === enum returns a false value + * for all of the selectors. + * + * If no arguments are given, returns an array containing all elements of + * +enum+ for which the given +block+ returns a false value. * * If no block is given, an Enumerator is returned instead. * + * ("aa".."zz").reject(/[ac-qst]/, /[vwxz]/, /^[by]/, /[ru]$/) + * #=> ["rb", "ry", "ub", "uy"] + * * (1..10).reject { |i| i % 3 == 0 } #=> [1, 2, 4, 5, 7, 8, 10] * * [1, 2, 3, 4, 5].reject { |num| num.even? } #=> [1, 3, 5] @@ -418,14 +444,27 @@ reject_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary)) */ static VALUE -enum_reject(VALUE obj) +enum_reject(int argc, VALUE *argv, VALUE obj) { VALUE ary; + NODE *memo; + VALUE items = Qnil; - RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size); + if (argc == 0) { + RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size); - ary = rb_ary_new(); - rb_block_call(obj, id_each, 0, 0, reject_i, ary); + ary = rb_ary_new(); + rb_block_call(obj, id_each, 0, 0, reject_iter_i, ary); + } + else { + rb_scan_args(argc, argv, "*", &items); + if (rb_block_given_p()) + rb_warn("given block not used"); + + ary = rb_ary_new(); + memo = NEW_MEMO(items, ary, 0); + rb_block_call(obj, id_each, 0, 0, reject_i, (VALUE)memo); + } return ary; } @@ -3093,7 +3132,7 @@ Init_Enumerable(void) rb_define_method(rb_mEnumerable, "find_index", enum_find_index, -1); rb_define_method(rb_mEnumerable, "find_all", enum_find_all, -1); rb_define_method(rb_mEnumerable, "select", enum_find_all, -1); - rb_define_method(rb_mEnumerable, "reject", enum_reject, 0); + rb_define_method(rb_mEnumerable, "reject", enum_reject, -1); rb_define_method(rb_mEnumerable, "collect", enum_collect, 0); rb_define_method(rb_mEnumerable, "map", enum_collect, 0); rb_define_method(rb_mEnumerable, "flat_map", enum_flat_map, 0); diff --git a/test/ruby/test_enum.rb b/test/ruby/test_enum.rb index 844b4c06ac58f3..f712d28bcccff1 100644 --- a/test/ruby/test_enum.rb +++ b/test/ruby/test_enum.rb @@ -102,7 +102,6 @@ def test_find_all assert_equal([1, 3, 1], @obj.find_all {|x| x % 2 == 1 }) cond = ->(x, i) { x % 2 == 1 } assert_equal([[1, 0], [3, 2], [1, 3]], @obj.each_with_index.find_all(&cond)) - assert_equal([1, 2, 3, 1, 2], @obj.find_all(Numeric)) assert_equal([2, 3, 2], @obj.find_all(3, 2)) end @@ -111,6 +110,8 @@ def test_reject assert_equal([2, 3, 2], @obj.reject {|x| x < 2 }) cond = ->(x, i) {x < 2} assert_equal([[2, 1], [3, 2], [2, 4]], @obj.each_with_index.reject(&cond)) + assert_equal([], @obj.reject(Numeric)) + assert_equal([1, 1], @obj.reject(3, 2)) end def test_to_a From 0744b3b8a158d8c15d7d26ef2efc7d0ce2150c1e Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Tue, 1 Apr 2014 20:41:41 -0700 Subject: [PATCH 4/4] Array#reject to take args and use ===; documentation; tests --- array.c | 77 ++++++++++++++++++++++++++++------------- test/ruby/test_array.rb | 2 ++ 2 files changed, 54 insertions(+), 25 deletions(-) diff --git a/array.c b/array.c index 010c06e9bafd18..4f4e649cde606d 100644 --- a/array.c +++ b/array.c @@ -2814,7 +2814,8 @@ rb_ary_select(int argc, VALUE *argv, VALUE ary) rb_ary_push(result, rb_ary_elt(ary, i)); } } - } else { + } + else { if (rb_block_given_p()) rb_warn("given block not used"); @@ -3095,20 +3096,6 @@ rb_ary_slice_bang(int argc, VALUE *argv, VALUE ary) return rb_ary_delete_at(ary, NUM2LONG(arg1)); } -static VALUE -ary_reject(VALUE orig, VALUE result) -{ - long i; - - for (i = 0; i < RARRAY_LEN(orig); i++) { - VALUE v = RARRAY_AREF(orig, i); - if (!RTEST(rb_yield(v))) { - rb_ary_push_1(result, v); - } - } - return result; -} - static VALUE ary_reject_bang(VALUE ary) { @@ -3155,25 +3142,65 @@ rb_ary_reject_bang(VALUE ary) /* * call-seq: * ary.reject {|item| block } -> new_ary + * ary.reject(selector, ...) -> new_ary * ary.reject -> Enumerator * - * Returns a new array containing the items in +self+ for which the given - * block is not +true+. + * If a list of selectors is given, returns a new array containing all + * elements of +ary+ for which selector === ary is a false value + * for any of the selectors. * - * See also Array#delete_if + * If no arguments are given, returns a new array containing all elements of + * +ary+ for which the given block returns a false value. * * If no block is given, an Enumerator is returned instead. + * + * %w{ foo bar baz quux }.reject(/ba/, /f/) #=> ["quux"] + * + * [1, "two", 3.14, {}, :five].reject(Numeric, Enumerable) #=> ["two", :five] + * + * [1,2,3,4,5].reject { |num| num.even? } #=> [1, 3, 5] + * + * a = %w{ a b c d e f } + * a.reject { |v| v =~ /[aeiou]/ } #=> ["b", "c", "d", "f"] + * + * See also Array#delete_if */ static VALUE -rb_ary_reject(VALUE ary) +rb_ary_reject(int argc, VALUE *argv, VALUE ary) { - VALUE rejected_ary; + VALUE result; + long i, j, should_reject; - RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length); - rejected_ary = rb_ary_new(); - ary_reject(ary, rejected_ary); - return rejected_ary; + if (argc == 0) { + RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length); + result = rb_ary_new(); + for (i = 0; i < RARRAY_LEN(ary); i++) { + if (!RTEST(rb_yield(RARRAY_AREF(ary, i)))) { + rb_ary_push_1(result, rb_ary_elt(ary, i)); + } + } + } + else { + if (rb_block_given_p()) + rb_warn("given block not used"); + + result = rb_ary_new(); + for (i = 0; i < RARRAY_LEN(ary); i++) { + should_reject = 1; + for (j = 0; j < argc; j++) { + if (RTEST(rb_funcall(argv[j], idEqq, 1, RARRAY_AREF(ary, i)))) { + should_reject = 0; + break; + } + } + + if (should_reject) + rb_ary_push_1(result, rb_ary_elt(ary, i)); + } + } + + return result; } /* @@ -5699,7 +5726,7 @@ Init_Array(void) rb_define_method(rb_cArray, "delete", rb_ary_delete, 1); rb_define_method(rb_cArray, "delete_at", rb_ary_delete_at_m, 1); rb_define_method(rb_cArray, "delete_if", rb_ary_delete_if, 0); - rb_define_method(rb_cArray, "reject", rb_ary_reject, 0); + rb_define_method(rb_cArray, "reject", rb_ary_reject, -1); rb_define_method(rb_cArray, "reject!", rb_ary_reject_bang, 0); rb_define_method(rb_cArray, "zip", rb_ary_zip, -1); rb_define_method(rb_cArray, "transpose", rb_ary_transpose, 0); diff --git a/test/ruby/test_array.rb b/test/ruby/test_array.rb index abaa9d67f17634..7444225c1c886a 100644 --- a/test/ruby/test_array.rb +++ b/test/ruby/test_array.rb @@ -2012,6 +2012,8 @@ def test_delete2 def test_reject assert_equal([1, 3], [0, 1, 2, 3].reject {|x| x % 2 == 0 }) + assert_equal(["quux"], ["foo", "bar", "baz", "quux"].reject(/ba/, /f/)) + assert_equal(["two", :bar], [1, "two", 3.1, {}, :bar].reject(Numeric, Enumerable)) end def test_zip