ruby-changes:62383
From: Nobuyoshi <ko1@a...>
Date: Thu, 23 Jul 2020 17:53:23 +0900 (JST)
Subject: [ruby-changes:62383] 6b3cff12f6 (master): Improved Enumerable::Lazy#flat_map
https://git.ruby-lang.org/ruby.git/commit/?id=6b3cff12f6 From 6b3cff12f6add831c678ce7a5288097714bc6850 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada <nobu@r...> Date: Wed, 22 Jul 2020 00:58:48 +0900 Subject: Improved Enumerable::Lazy#flat_map | |compare-ruby|built-ruby| |:-------|-----------:|---------:| |num3 | 96.333k| 160.732k| | | -| 1.67x| |num10 | 96.615k| 159.150k| | | -| 1.65x| |ary2 | 103.836k| 172.787k| | | -| 1.66x| |ary10 | 109.249k| 177.252k| | | -| 1.62x| |ary20 | 106.628k| 177.371k| | | -| 1.66x| |ary50 | 107.135k| 162.282k| | | -| 1.51x| |ary100 | 106.513k| 177.626k| | | -| 1.67x| diff --git a/benchmark/enum_lazy_flat_map.yml b/benchmark/enum_lazy_flat_map.yml new file mode 100644 index 0000000..0ee390a --- /dev/null +++ b/benchmark/enum_lazy_flat_map.yml @@ -0,0 +1,16 @@ https://github.com/ruby/ruby/blob/trunk/benchmark/enum_lazy_flat_map.yml#L1 +prelude: | + num = (1..).lazy.take(100) + ary2 = [[1,2]].cycle.lazy.take(10) + ary10 = [[*1..10]].cycle.lazy.take(10) + ary20 = [[*1..20]].cycle.lazy.take(10) + ary50 = [[*1..50]].cycle.lazy.take(10) + ary100 = [[*1..100]].cycle.lazy.take(10) + +benchmark: + num3: num.flat_map {|x| x}.take(3).to_a + num10: num.flat_map {|x| x}.take(3).to_a + ary2: ary2.flat_map {|x| x}.take(3).to_a + ary10: ary10.flat_map {|x| x}.take(3).to_a + ary20: ary20.flat_map {|x| x}.take(3).to_a + ary50: ary50.flat_map {|x| x}.take(3).to_a + ary100: ary100.flat_map {|x| x}.take(3).to_a diff --git a/enumerator.c b/enumerator.c index d10f239..c560c16 100644 --- a/enumerator.c +++ b/enumerator.c @@ -1605,6 +1605,7 @@ lazy_init_block_i(RB_BLOCK_CALL_FUNC_ARGLIST(val, m)) https://github.com/ruby/ruby/blob/trunk/enumerator.c#L1605 #define LAZY_MEMO_BREAK_P(memo) ((memo)->memo_flags & LAZY_MEMO_BREAK) #define LAZY_MEMO_PACKED_P(memo) ((memo)->memo_flags & LAZY_MEMO_PACKED) #define LAZY_MEMO_SET_BREAK(memo) ((memo)->memo_flags |= LAZY_MEMO_BREAK) +#define LAZY_MEMO_RESET_BREAK(memo) ((memo)->memo_flags &= ~LAZY_MEMO_BREAK) #define LAZY_MEMO_SET_VALUE(memo, value) MEMO_V2_SET(memo, value) #define LAZY_MEMO_SET_PACKED(memo) ((memo)->memo_flags |= LAZY_MEMO_PACKED) #define LAZY_MEMO_RESET_PACKED(memo) ((memo)->memo_flags &= ~LAZY_MEMO_PACKED) @@ -2058,58 +2059,57 @@ lazy_map(VALUE obj) https://github.com/ruby/ruby/blob/trunk/enumerator.c#L2059 return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_map_funcs); } +struct flat_map_i_arg { + struct MEMO *result; + long index; +}; + static VALUE -lazy_flat_map_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, yielder)) +lazy_flat_map_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, y)) { - VALUE arg = rb_enum_values_pack(argc, argv); + struct flat_map_i_arg *arg = (struct flat_map_i_arg *)y; - return rb_funcallv(yielder, idLTLT, 1, &arg); + return lazy_yielder_yield(arg->result, arg->index, argc, argv); } -static VALUE -lazy_flat_map_each(VALUE obj, VALUE yielder) +static struct MEMO * +lazy_flat_map_proc(VALUE proc_entry, struct MEMO *result, VALUE memos, long memo_index) { - rb_block_call(obj, id_each, 0, 0, lazy_flat_map_i, yielder); - return Qnil; -} + VALUE value = lazyenum_yield_values(proc_entry, result); + VALUE ary = 0; + const long proc_index = memo_index + 1; + int break_p = LAZY_MEMO_BREAK_P(result); -static VALUE -lazy_flat_map_to_ary(VALUE obj, VALUE yielder) -{ - VALUE ary = rb_check_array_type(obj); - if (NIL_P(ary)) { - rb_funcall(yielder, idLTLT, 1, obj); + if (RB_TYPE_P(value, T_ARRAY)) { + ary = value; } - else { - long i; - for (i = 0; i < RARRAY_LEN(ary); i++) { - rb_funcall(yielder, idLTLT, 1, RARRAY_AREF(ary, i)); - } + else if (rb_respond_to(value, id_force) && rb_respond_to(value, id_each)) { + struct flat_map_i_arg arg = {.result = result, .index = proc_index}; + LAZY_MEMO_RESET_BREAK(result); + rb_block_call(value, id_each, 0, 0, lazy_flat_map_i, (VALUE)&arg); + if (break_p) LAZY_MEMO_SET_BREAK(result); + return 0; } - return Qnil; -} -static VALUE -lazy_flat_map_proc(RB_BLOCK_CALL_FUNC_ARGLIST(val, m)) -{ - VALUE result = rb_yield_values2(argc - 1, &argv[1]); - if (RB_TYPE_P(result, T_ARRAY)) { - long i; - for (i = 0; i < RARRAY_LEN(result); i++) { - rb_funcall(argv[0], idLTLT, 1, RARRAY_AREF(result, i)); - } - } - else { - if (rb_respond_to(result, id_force) && rb_respond_to(result, id_each)) { - lazy_flat_map_each(result, argv[0]); - } - else { - lazy_flat_map_to_ary(result, argv[0]); - } + if (ary || !NIL_P(ary = rb_check_array_type(value))) { + long i; + LAZY_MEMO_RESET_BREAK(result); + for (i = 0; i + 1 < RARRAY_LEN(ary); i++) { + lazy_yielder_yield(result, proc_index, 1, &RARRAY_AREF(ary, i)); + } + if (break_p) LAZY_MEMO_SET_BREAK(result); + if (i >= RARRAY_LEN(ary)) return 0; + value = RARRAY_AREF(ary, i); } - return Qnil; + LAZY_MEMO_SET_VALUE(result, value); + LAZY_MEMO_RESET_PACKED(result); + return result; } +static const lazyenum_funcs lazy_flat_map_funcs = { + lazy_flat_map_proc, 0, +}; + /* * call-seq: * lazy.collect_concat { |obj| block } -> a_lazy_enumerator @@ -2140,9 +2140,7 @@ lazy_flat_map(VALUE obj) https://github.com/ruby/ruby/blob/trunk/enumerator.c#L2140 rb_raise(rb_eArgError, "tried to call lazy flat_map without a block"); } - return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj, - lazy_flat_map_proc, 0), - Qnil, 0); + return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_flat_map_funcs); } static struct MEMO * diff --git a/test/ruby/test_lazy_enumerator.rb b/test/ruby/test_lazy_enumerator.rb index 6e5c171..3f5a055 100644 --- a/test/ruby/test_lazy_enumerator.rb +++ b/test/ruby/test_lazy_enumerator.rb @@ -160,6 +160,10 @@ class TestLazyEnumerator < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_lazy_enumerator.rb#L160 assert_equal([{?a=>97}, {?b=>98}, {?c=>99}], [?a, ?b, ?c].lazy.flat_map {|x| {x=>x.ord}}.force) end + def test_flat_map_take + assert_equal([1,2]*3, [[1,2]].cycle.lazy.take(3).flat_map {|x| x}.to_a) + end + def test_reject a = Step.new(1..6) assert_equal(4, a.reject {|x| x < 4}.first) -- cgit v0.10.2 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/