From ebf86a24b058f9aa3f0b3765cb1bf3088005fcc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=9F=E6=B9=96=E6=96=B0?= Date: Sat, 13 Jan 2024 17:36:02 +0900 Subject: [PATCH 1/3] Enumerable#chunk implementation --- mrbgems/mruby-enum-ext/mrbgem.rake | 2 + mrbgems/mruby-enum-ext/mrblib/enum.rb | 68 +++++++++++++++++++++++++++ mrbgems/mruby-enum-ext/test/enum.rb | 49 +++++++++++++++++++ 3 files changed, 119 insertions(+) diff --git a/mrbgems/mruby-enum-ext/mrbgem.rake b/mrbgems/mruby-enum-ext/mrbgem.rake index d5816b80f..9394bc514 100644 --- a/mrbgems/mruby-enum-ext/mrbgem.rake +++ b/mrbgems/mruby-enum-ext/mrbgem.rake @@ -2,4 +2,6 @@ MRuby::Gem::Specification.new('mruby-enum-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Enumerable module extension' + + spec.add_dependency 'mruby-enumerator', core: 'mruby-enumerator' end diff --git a/mrbgems/mruby-enum-ext/mrblib/enum.rb b/mrbgems/mruby-enum-ext/mrblib/enum.rb index 2c9e8d3a5..e3fa57aac 100644 --- a/mrbgems/mruby-enum-ext/mrblib/enum.rb +++ b/mrbgems/mruby-enum-ext/mrblib/enum.rb @@ -880,4 +880,72 @@ module Enumerable end result end + + ## + # call-seq: + # enum.chunk -> enumerator + # enum.chunk { |arr| block } -> enumerator + # + # Each element in the returned enumerator is a 2-element array consisting of: + # + # - A value returned by the block. + # - An array ("chunk") containing the element for which that value was returned, + # and all following elements for which the block returned the same value: + # + # So that: + # + # - Each block return value that is different from its predecessor + # begins a new chunk. + # - Each block return value that is the same as its predecessor + # continues the same chunk. + # + # Example: + # + # e = (0..10).chunk {|i| (i / 3).floor } # => # + # # The enumerator elements. + # e.next # => [0, [0, 1, 2]] + # e.next # => [1, [3, 4, 5]] + # e.next # => [2, [6, 7, 8]] + # e.next # => [3, [9, 10]] + # + # You can use the special symbol :_alone to force an element + # into its own separate chuck: + # + # a = [0, 0, 1, 1] + # e = a.chunk{|i| i.even? ? :_alone : true } + # e.to_a # => [[:_alone, [0]], [:_alone, [0]], [true, [1, 1]]] + # + # You can use the special symbol :_separator or +nil+ + # to force an element to be ignored (not included in any chunk): + # + # a = [0, 0, -1, 1, 1] + # e = a.chunk{|i| i < 0 ? :_separator : true } + # e.to_a # => [[true, [0, 0]], [true, [1, 1]]] + def chunk(&block) + return to_enum :chunk unless block + + enum = self + Enumerator.new do |y| + last_value, arr = nil, [] + enum.each do |element| + value = block.call(element) + case value + when :_alone + y.yield [last_value, arr] if arr.size > 0 + y.yield [value, [element]] + last_value, arr = nil, [] + when :_separator, nil + y.yield [last_value, arr] if arr.size > 0 + last_value, arr = nil, [] + when last_value + arr << element + else + raise 'symbols beginning with an underscore are reserved' if value.is_a?(Symbol) && value.to_s[0] == '_' + y.yield [last_value, arr] if arr.size > 0 + last_value, arr = value, [element] + end + end + y.yield [last_value, arr] if arr.size > 0 + end + end end diff --git a/mrbgems/mruby-enum-ext/test/enum.rb b/mrbgems/mruby-enum-ext/test/enum.rb index 31181fe1a..759c0ead1 100644 --- a/mrbgems/mruby-enum-ext/test/enum.rb +++ b/mrbgems/mruby-enum-ext/test/enum.rb @@ -195,3 +195,52 @@ end assert("Enumerable#tally") do assert_equal({"a"=>1, "b"=>2, "c"=>1}, ["a", "b", "c", "b"].tally) end + +assert("Enumerable#chunk") do + chunk = [1, 2, 3, 1, 2].chunk + assert_equal Enumerator, chunk.class + result = chunk.with_index { |elt, i| elt - i }.to_a + assert_equal [[1, [1, 2, 3]], [-2, [1, 2]]], result + + assert_equal Enumerator, [].chunk {}.class + + e = [1, 2, 3] + recorded = [] + e.chunk { |x| recorded << x }.to_a + assert_equal [1, 2, 3], recorded + + e = [1, 2, 3, 2, 3, 2, 1] + result = e.chunk { |x| x < 3 && 1 || 0 }.to_a + assert_equal [[1, [1, 2]], [0, [3]], [1, [2]], [0, [3]], [1, [2, 1]]], result + + e = [1, 2, 3] + assert_equal [[1, 2], [3]], e.chunk { |x| x > 2 }.map(&:last) + + e = [1, 2, 3, 2, 1] + result = e.chunk { |x| x < 2 && :_alone }.to_a + assert_equal [[:_alone, [1]], [false, [2, 3, 2]], [:_alone, [1]]], result + + e = [[1, 2]] + inner_value = [] + e.chunk { |*x| inner_value << x }.to_a + assert_equal [[[1, 2]]], inner_value + + e = [1, 2, 3, 3, 2, 1] + result = e.chunk { |x| x == 2 ? :_separator : 1 }.to_a + assert_equal [[1, [1]], [1, [3, 3]], [1, [1]]], result + + e = [1, 2, 3, 2, 1] + result = e.chunk { |x| x == 2 ? nil : 1 }.to_a + assert_equal [[1, [1]], [1, [3]], [1, [1]]], result + + + e = [1, 2, 3, 2, 1] + assert_raise(RuntimeError) { e.chunk { |x| :_arbitrary }.to_a } + + e = [1, 2, 3] + assert_raise(ArgumentError) { e.chunk(1) {} } + + e = [1, 2, 3, 2, 1] + enum = e.chunk { |x| true } + assert_nil enum.size +end From d001974439ec2bde6e5ca48a1c776a69e727b3aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=9F=E6=B9=96=E6=96=B0?= Date: Mon, 22 Jan 2024 18:26:01 +0900 Subject: [PATCH 2/3] Revert "Enumerable#chunk implementation" This reverts commit ebf86a24b058f9aa3f0b3765cb1bf3088005fcc0. --- mrbgems/mruby-enum-ext/mrbgem.rake | 2 - mrbgems/mruby-enum-ext/mrblib/enum.rb | 68 --------------------------- mrbgems/mruby-enum-ext/test/enum.rb | 49 ------------------- 3 files changed, 119 deletions(-) diff --git a/mrbgems/mruby-enum-ext/mrbgem.rake b/mrbgems/mruby-enum-ext/mrbgem.rake index 9394bc514..d5816b80f 100644 --- a/mrbgems/mruby-enum-ext/mrbgem.rake +++ b/mrbgems/mruby-enum-ext/mrbgem.rake @@ -2,6 +2,4 @@ MRuby::Gem::Specification.new('mruby-enum-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Enumerable module extension' - - spec.add_dependency 'mruby-enumerator', core: 'mruby-enumerator' end diff --git a/mrbgems/mruby-enum-ext/mrblib/enum.rb b/mrbgems/mruby-enum-ext/mrblib/enum.rb index e3fa57aac..2c9e8d3a5 100644 --- a/mrbgems/mruby-enum-ext/mrblib/enum.rb +++ b/mrbgems/mruby-enum-ext/mrblib/enum.rb @@ -880,72 +880,4 @@ module Enumerable end result end - - ## - # call-seq: - # enum.chunk -> enumerator - # enum.chunk { |arr| block } -> enumerator - # - # Each element in the returned enumerator is a 2-element array consisting of: - # - # - A value returned by the block. - # - An array ("chunk") containing the element for which that value was returned, - # and all following elements for which the block returned the same value: - # - # So that: - # - # - Each block return value that is different from its predecessor - # begins a new chunk. - # - Each block return value that is the same as its predecessor - # continues the same chunk. - # - # Example: - # - # e = (0..10).chunk {|i| (i / 3).floor } # => # - # # The enumerator elements. - # e.next # => [0, [0, 1, 2]] - # e.next # => [1, [3, 4, 5]] - # e.next # => [2, [6, 7, 8]] - # e.next # => [3, [9, 10]] - # - # You can use the special symbol :_alone to force an element - # into its own separate chuck: - # - # a = [0, 0, 1, 1] - # e = a.chunk{|i| i.even? ? :_alone : true } - # e.to_a # => [[:_alone, [0]], [:_alone, [0]], [true, [1, 1]]] - # - # You can use the special symbol :_separator or +nil+ - # to force an element to be ignored (not included in any chunk): - # - # a = [0, 0, -1, 1, 1] - # e = a.chunk{|i| i < 0 ? :_separator : true } - # e.to_a # => [[true, [0, 0]], [true, [1, 1]]] - def chunk(&block) - return to_enum :chunk unless block - - enum = self - Enumerator.new do |y| - last_value, arr = nil, [] - enum.each do |element| - value = block.call(element) - case value - when :_alone - y.yield [last_value, arr] if arr.size > 0 - y.yield [value, [element]] - last_value, arr = nil, [] - when :_separator, nil - y.yield [last_value, arr] if arr.size > 0 - last_value, arr = nil, [] - when last_value - arr << element - else - raise 'symbols beginning with an underscore are reserved' if value.is_a?(Symbol) && value.to_s[0] == '_' - y.yield [last_value, arr] if arr.size > 0 - last_value, arr = value, [element] - end - end - y.yield [last_value, arr] if arr.size > 0 - end - end end diff --git a/mrbgems/mruby-enum-ext/test/enum.rb b/mrbgems/mruby-enum-ext/test/enum.rb index 759c0ead1..31181fe1a 100644 --- a/mrbgems/mruby-enum-ext/test/enum.rb +++ b/mrbgems/mruby-enum-ext/test/enum.rb @@ -195,52 +195,3 @@ end assert("Enumerable#tally") do assert_equal({"a"=>1, "b"=>2, "c"=>1}, ["a", "b", "c", "b"].tally) end - -assert("Enumerable#chunk") do - chunk = [1, 2, 3, 1, 2].chunk - assert_equal Enumerator, chunk.class - result = chunk.with_index { |elt, i| elt - i }.to_a - assert_equal [[1, [1, 2, 3]], [-2, [1, 2]]], result - - assert_equal Enumerator, [].chunk {}.class - - e = [1, 2, 3] - recorded = [] - e.chunk { |x| recorded << x }.to_a - assert_equal [1, 2, 3], recorded - - e = [1, 2, 3, 2, 3, 2, 1] - result = e.chunk { |x| x < 3 && 1 || 0 }.to_a - assert_equal [[1, [1, 2]], [0, [3]], [1, [2]], [0, [3]], [1, [2, 1]]], result - - e = [1, 2, 3] - assert_equal [[1, 2], [3]], e.chunk { |x| x > 2 }.map(&:last) - - e = [1, 2, 3, 2, 1] - result = e.chunk { |x| x < 2 && :_alone }.to_a - assert_equal [[:_alone, [1]], [false, [2, 3, 2]], [:_alone, [1]]], result - - e = [[1, 2]] - inner_value = [] - e.chunk { |*x| inner_value << x }.to_a - assert_equal [[[1, 2]]], inner_value - - e = [1, 2, 3, 3, 2, 1] - result = e.chunk { |x| x == 2 ? :_separator : 1 }.to_a - assert_equal [[1, [1]], [1, [3, 3]], [1, [1]]], result - - e = [1, 2, 3, 2, 1] - result = e.chunk { |x| x == 2 ? nil : 1 }.to_a - assert_equal [[1, [1]], [1, [3]], [1, [1]]], result - - - e = [1, 2, 3, 2, 1] - assert_raise(RuntimeError) { e.chunk { |x| :_arbitrary }.to_a } - - e = [1, 2, 3] - assert_raise(ArgumentError) { e.chunk(1) {} } - - e = [1, 2, 3, 2, 1] - enum = e.chunk { |x| true } - assert_nil enum.size -end From ab21813100803ca94f9e18cbad3e2e2eb9dea15a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=9F=E6=B9=96=E6=96=B0?= Date: Mon, 22 Jan 2024 18:31:28 +0900 Subject: [PATCH 3/3] Move Enumerable#chunk to mruby-enumerator --- mrbgems/mruby-enumerator/mrblib/enumerator.rb | 68 +++++++++++++++++++ mrbgems/mruby-enumerator/test/enumerator.rb | 49 +++++++++++++ 2 files changed, 117 insertions(+) diff --git a/mrbgems/mruby-enumerator/mrblib/enumerator.rb b/mrbgems/mruby-enumerator/mrblib/enumerator.rb index faaabf18a..44d84865a 100644 --- a/mrbgems/mruby-enumerator/mrblib/enumerator.rb +++ b/mrbgems/mruby-enumerator/mrblib/enumerator.rb @@ -701,4 +701,72 @@ module Enumerable result end + + ## + # call-seq: + # enum.chunk -> enumerator + # enum.chunk { |arr| block } -> enumerator + # + # Each element in the returned enumerator is a 2-element array consisting of: + # + # - A value returned by the block. + # - An array ("chunk") containing the element for which that value was returned, + # and all following elements for which the block returned the same value: + # + # So that: + # + # - Each block return value that is different from its predecessor + # begins a new chunk. + # - Each block return value that is the same as its predecessor + # continues the same chunk. + # + # Example: + # + # e = (0..10).chunk {|i| (i / 3).floor } # => # + # # The enumerator elements. + # e.next # => [0, [0, 1, 2]] + # e.next # => [1, [3, 4, 5]] + # e.next # => [2, [6, 7, 8]] + # e.next # => [3, [9, 10]] + # + # You can use the special symbol :_alone to force an element + # into its own separate chuck: + # + # a = [0, 0, 1, 1] + # e = a.chunk{|i| i.even? ? :_alone : true } + # e.to_a # => [[:_alone, [0]], [:_alone, [0]], [true, [1, 1]]] + # + # You can use the special symbol :_separator or +nil+ + # to force an element to be ignored (not included in any chunk): + # + # a = [0, 0, -1, 1, 1] + # e = a.chunk{|i| i < 0 ? :_separator : true } + # e.to_a # => [[true, [0, 0]], [true, [1, 1]]] + def chunk(&block) + return to_enum :chunk unless block + + enum = self + Enumerator.new do |y| + last_value, arr = nil, [] + enum.each do |element| + value = block.call(element) + case value + when :_alone + y.yield [last_value, arr] if arr.size > 0 + y.yield [value, [element]] + last_value, arr = nil, [] + when :_separator, nil + y.yield [last_value, arr] if arr.size > 0 + last_value, arr = nil, [] + when last_value + arr << element + else + raise 'symbols beginning with an underscore are reserved' if value.is_a?(Symbol) && value.to_s[0] == '_' + y.yield [last_value, arr] if arr.size > 0 + last_value, arr = value, [element] + end + end + y.yield [last_value, arr] if arr.size > 0 + end + end end diff --git a/mrbgems/mruby-enumerator/test/enumerator.rb b/mrbgems/mruby-enumerator/test/enumerator.rb index 3e0c6c3be..4baf20c25 100644 --- a/mrbgems/mruby-enumerator/test/enumerator.rb +++ b/mrbgems/mruby-enumerator/test/enumerator.rb @@ -598,3 +598,52 @@ assert 'Enumerator.produce' do ], enum.to_a } end + +assert("Enumerable#chunk") do + chunk = [1, 2, 3, 1, 2].chunk + assert_equal Enumerator, chunk.class + result = chunk.with_index { |elt, i| elt - i }.to_a + assert_equal [[1, [1, 2, 3]], [-2, [1, 2]]], result + + assert_equal Enumerator, [].chunk {}.class + + e = [1, 2, 3] + recorded = [] + e.chunk { |x| recorded << x }.to_a + assert_equal [1, 2, 3], recorded + + e = [1, 2, 3, 2, 3, 2, 1] + result = e.chunk { |x| x < 3 && 1 || 0 }.to_a + assert_equal [[1, [1, 2]], [0, [3]], [1, [2]], [0, [3]], [1, [2, 1]]], result + + e = [1, 2, 3] + assert_equal [[1, 2], [3]], e.chunk { |x| x > 2 }.map(&:last) + + e = [1, 2, 3, 2, 1] + result = e.chunk { |x| x < 2 && :_alone }.to_a + assert_equal [[:_alone, [1]], [false, [2, 3, 2]], [:_alone, [1]]], result + + e = [[1, 2]] + inner_value = [] + e.chunk { |*x| inner_value << x }.to_a + assert_equal [[[1, 2]]], inner_value + + e = [1, 2, 3, 3, 2, 1] + result = e.chunk { |x| x == 2 ? :_separator : 1 }.to_a + assert_equal [[1, [1]], [1, [3, 3]], [1, [1]]], result + + e = [1, 2, 3, 2, 1] + result = e.chunk { |x| x == 2 ? nil : 1 }.to_a + assert_equal [[1, [1]], [1, [3]], [1, [1]]], result + + + e = [1, 2, 3, 2, 1] + assert_raise(RuntimeError) { e.chunk { |x| :_arbitrary }.to_a } + + e = [1, 2, 3] + assert_raise(ArgumentError) { e.chunk(1) {} } + + e = [1, 2, 3, 2, 1] + enum = e.chunk { |x| true } + assert_nil enum.size +end