在ruby中交错两个枚举的最佳方式



我正在寻找一种更优雅的方法,将两个具有给定比率的SQL结果集混合在一起。在它们中的每一个中,我希望它们以相同的顺序进行处理,但我希望交错处理以实现所需的混合。

我意识到这可以成为一个非常通用的方法,使用两个枚举并生成要处理的项,所以我写了这个方法,我同时感到非常自豪(很好的通用解决方案(,也感到非常羞愧。

def combine_enums_with_ratio(enum_a, enum_b, desired_ratio)
a_count = 1
b_count = 1
a_finished = false
b_finished = false
loop do
ratio_so_far = a_count / b_count.to_f
if !a_finished && (b_finished || ratio_so_far <= desired_ratio)
begin
yield enum_a.next
a_count += 1
rescue StopIteration
a_finished = true
end
end
if !b_finished && (a_finished || ratio_so_far > desired_ratio)
begin
yield enum_b.next
b_count += 1
rescue StopIteration
b_finished = true
end
end
break if a_finished && b_finished
end
end

很惭愧,因为它是用一种非常命令式的风格写的。看起来不是很邋遢。也许有一种方法可以使用ruby的一个很好的声明性循环方法,但它们似乎无法像这样打开两个枚举。因此,我相信我不得不拯救一个异常,作为控制流的一部分,这感觉非常肮脏。我缺少java的hasNext()方法。

有更好的方法吗?

我确实发现了一个关于比较枚举器的类似问题:Ruby-优雅地比较两个枚举器。一些紧凑的答案,但并不是特别能解决它,我的问题涉及不相等的长度和不相等的屈服似乎更棘手。

这里有一个更短、更通用的方法:

def combine_enums_with_ratio(ratios)
return enum_for(__method__, ratios) unless block_given?
counts = ratios.transform_values { |value| Rational(1, value) }
until counts.empty?
begin
enum, _ = counts.min_by(&:last)
yield enum.next
counts[enum] += Rational(1, ratios[enum])
rescue StopIteration
counts.delete(enum)
end
end
end

它采用enum => ratio对的散列,而不是两个枚举。

首先,它使用比率的倒数创建counts散列,即enum_a => 3, enum_b => 2变为:

counts = { enum_a => 1/3r, enum_b => 1/2r }

然后,在一个循环中,它获取散列的最小值,在上面的例子中是enum_a。它产生其next值并增加其counts比值:

counts[enum_a] += 1/3r
counts #=> {:enum_a=>(2/3), :enum_b=>(1/2)}

在下一次迭代中,enum_b的值最小,因此将产生其next值,并增加其比率:

counts[enum_b] += 1/2r
counts #=> {:enum_a=>(2/3), :enum_b=>(1/1)}

如果将enum_a递增(1/3),将enum_b递增(1/2),它们元素的产率比将为3:2。

最后,rescue子句处理元素用完的枚举。如果发生这种情况,则会从counts哈希中删除该枚举。

一旦counts散列为空,循环就会停止。

使用3个枚举的示例:

enum_a = (1..10).each
enum_b = ('a'..'f').each
enum_c = %i[foo bar baz].each
combine_enums_with_ratio(enum_a => 3, enum_b => 2, enum_c => 1).to_a
#=> [1, "a", 2, 3, "b", :foo, 4, "c", 5, 6, "d", :bar, 7, "e", 8, 9, "f", :baz, 10]
#    <--------------------->  <--------------------->  <--------------------->
#             3:2:1                    3:2:1                     3:2:1

最新更新