From 4fa6c16b289d094a7dfb061b24d7f0c5f6e5e7cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Mon, 8 May 2023 13:10:46 +0200 Subject: [PATCH 1/4] Fix return type of `Iterator#chunk` and `Enumerable#chunks` without `Drop` --- spec/std/enumerable_spec.cr | 8 +++++--- src/enumerable.cr | 41 +++++++++++++++++++++++++------------ src/iterator.cr | 18 ++++++++-------- 3 files changed, 43 insertions(+), 24 deletions(-) diff --git a/spec/std/enumerable_spec.cr b/spec/std/enumerable_spec.cr index 41ba27e5e1f8..435a056191e3 100644 --- a/spec/std/enumerable_spec.cr +++ b/spec/std/enumerable_spec.cr @@ -177,6 +177,7 @@ describe "Enumerable" do it "drop all" do result = [1, 2].chunk { Enumerable::Chunk::Drop }.to_a + result.should be_a(Array(Tuple(NoReturn, Array(Int32)))) result.size.should eq 0 end @@ -192,14 +193,14 @@ describe "Enumerable" do it "reuses true" do iter = [1, 1, 2, 3, 3].chunk(reuse: true, &.itself) - a = iter.next.as(Tuple) + a = iter.next.should be_a(Tuple(Int32, Array(Int32))) a.should eq({1, [1, 1]}) - b = iter.next.as(Tuple) + b = iter.next.should be_a(Tuple(Int32, Array(Int32))) b.should eq({2, [2]}) b[1].should be(a[1]) - c = iter.next.as(Tuple) + c = iter.next.should be_a(Tuple(Int32, Array(Int32))) c.should eq({3, [3, 3]}) c[1].should be(a[1]) end @@ -239,6 +240,7 @@ describe "Enumerable" do it "drop all" do result = [1, 2].chunks { Enumerable::Chunk::Drop } + result.should be_a(Array(Tuple(NoReturn, Array(Int32)))) result.size.should eq 0 end diff --git a/src/enumerable.cr b/src/enumerable.cr index d71d93ea48c7..4a5b44892871 100644 --- a/src/enumerable.cr +++ b/src/enumerable.cr @@ -131,8 +131,8 @@ module Enumerable(T) # # See also: `Iterator#chunk`. def chunks(&block : T -> U) forall U - res = [] of Tuple(U, Array(T)) - chunks_internal(block) { |k, v| res << {k, v} } + res = [] of Tuple(typeof(Chunk.element_type(self, block)), Array(T)) + chunks_internal(block) { |*kv| res << kv } res end @@ -166,7 +166,6 @@ module Enumerable(T) end def init(key, val) - return if key == Drop @key = key if @reuse @@ -190,33 +189,49 @@ module Enumerable(T) def same_as?(key) : Bool return false unless @initialized - return false if key.in?(Alone, Drop) + return false if key.is_a?(Alone.class) || key.is_a?(Drop.class) @key == key end + def acc(key, val, &) + if same_as?(key) + add(val) + else + if tuple = fetch + yield *tuple + end + + init(key, val) unless key.is_a?(Drop.class) + end + end + def reset @initialized = false @data.clear end end + + def self.element_type(ary, block) + ary.each do |item| + key = block.call(item) + ::raise "" if key.is_a?(Drop.class) + return key + end + ::raise "" + end end private def chunks_internal(original_block : T -> U, &) forall U - acc = Chunk::Accumulator(T, U).new + acc = Chunk::Accumulator(T, typeof(Chunk.element_type(self, original_block))).new each do |val| key = original_block.call(val) - if acc.same_as?(key) - acc.add(val) - else - if tuple = acc.fetch - yield(*tuple) - end - acc.init(key, val) + acc.acc(key, val) do |*tuple| + yield *tuple end end if tuple = acc.fetch - yield(*tuple) + yield *tuple end end diff --git a/src/iterator.cr b/src/iterator.cr index 22b215440065..3029191ade5a 100644 --- a/src/iterator.cr +++ b/src/iterator.cr @@ -1455,21 +1455,22 @@ module Iterator(T) # # See also: `Enumerable#chunks`. def chunk(reuse = false, &block : T -> U) forall T, U - ChunkIterator(typeof(self), T, U).new(self, reuse, &block) + ChunkIterator(typeof(self), T, U, typeof(::Enumerable::Chunk.element_type(self, block))).new(self, reuse, &block) end - private class ChunkIterator(I, T, U) - include Iterator(Tuple(U, Array(T))) + private class ChunkIterator(I, T, U, V) + include Iterator(Tuple(V, Array(T))) @iterator : I - @init : {U, T}? + @init : {V, T}? def initialize(@iterator : Iterator(T), reuse, &@original_block : T -> U) - @acc = Enumerable::Chunk::Accumulator(T, U).new(reuse) + @acc = ::Enumerable::Chunk::Accumulator(T, V).new(reuse) end def next if init = @init - @acc.init(*init) + k, v = init + @acc.init(k, v) @init = nil end @@ -1481,10 +1482,10 @@ module Iterator(T) else tuple = @acc.fetch if tuple - @init = {key, val} + @init = {key, val} unless key.is_a?(::Enumerable::Chunk::Drop.class) return tuple else - @acc.init(key, val) + @acc.init(key, val) unless key.is_a?(::Enumerable::Chunk::Drop.class) end end end @@ -1492,6 +1493,7 @@ module Iterator(T) if tuple = @acc.fetch return tuple end + stop end From ed8b82bb15428ab86243a72c448c0f8194413c44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Fri, 26 May 2023 11:35:11 +0200 Subject: [PATCH 2/4] Remove unused method --- src/iterator.cr | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/iterator.cr b/src/iterator.cr index 3029191ade5a..cc84ca94dd6a 100644 --- a/src/iterator.cr +++ b/src/iterator.cr @@ -1496,12 +1496,6 @@ module Iterator(T) stop end - - private def init_state - @init = nil - @acc.reset - self - end end # Returns an iterator over chunks of elements, where each From 5fb051672461d2da5d271b33cbbfbef5e76d2c95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Mon, 5 Jun 2023 21:04:21 +0200 Subject: [PATCH 3/4] Remove unused method --- src/enumerable.cr | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/enumerable.cr b/src/enumerable.cr index 4a5b44892871..fbd7872d6679 100644 --- a/src/enumerable.cr +++ b/src/enumerable.cr @@ -204,11 +204,6 @@ module Enumerable(T) init(key, val) unless key.is_a?(Drop.class) end end - - def reset - @initialized = false - @data.clear - end end def self.element_type(ary, block) From 36d5b95cc6974710abb67a4610b2f3cbe7c3d8e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Mon, 5 Jun 2023 21:05:45 +0200 Subject: [PATCH 4/4] `.element_type` -> `.key_type` --- src/enumerable.cr | 6 +++--- src/iterator.cr | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/enumerable.cr b/src/enumerable.cr index fbd7872d6679..d7f978bd088d 100644 --- a/src/enumerable.cr +++ b/src/enumerable.cr @@ -131,7 +131,7 @@ module Enumerable(T) # # See also: `Iterator#chunk`. def chunks(&block : T -> U) forall U - res = [] of Tuple(typeof(Chunk.element_type(self, block)), Array(T)) + res = [] of Tuple(typeof(Chunk.key_type(self, block)), Array(T)) chunks_internal(block) { |*kv| res << kv } res end @@ -206,7 +206,7 @@ module Enumerable(T) end end - def self.element_type(ary, block) + def self.key_type(ary, block) ary.each do |item| key = block.call(item) ::raise "" if key.is_a?(Drop.class) @@ -217,7 +217,7 @@ module Enumerable(T) end private def chunks_internal(original_block : T -> U, &) forall U - acc = Chunk::Accumulator(T, typeof(Chunk.element_type(self, original_block))).new + acc = Chunk::Accumulator(T, typeof(Chunk.key_type(self, original_block))).new each do |val| key = original_block.call(val) acc.acc(key, val) do |*tuple| diff --git a/src/iterator.cr b/src/iterator.cr index cc84ca94dd6a..1d7d54cdcb58 100644 --- a/src/iterator.cr +++ b/src/iterator.cr @@ -1455,7 +1455,7 @@ module Iterator(T) # # See also: `Enumerable#chunks`. def chunk(reuse = false, &block : T -> U) forall T, U - ChunkIterator(typeof(self), T, U, typeof(::Enumerable::Chunk.element_type(self, block))).new(self, reuse, &block) + ChunkIterator(typeof(self), T, U, typeof(::Enumerable::Chunk.key_type(self, block))).new(self, reuse, &block) end private class ChunkIterator(I, T, U, V)