我正在寻找一种方法,在ruby中获取一个数组,数组中的两个索引,然后返回一个可枚举的对象,这个对象将按顺序产生两个索引之间的所有元素。但出于性能考虑,我想这样做要满足以下两个条件。
array[i..j].to_enum
例如,因为 array[i..j]
正在创建一个新的数组。我想知道是否有办法使用标准库的枚举或数组功能来实现这个功能,而不需要显式地创建我自己的自定义枚举器。
我所寻找的是一种更简洁的方法来创建下面的枚举器。
def enum_slice(array, i, j)
Enumerator.new do |y|
while i <= j
y << array[i] # this is confusing syntax for yield (see here: https://ruby-doc.org/core-2.6/Enumerator.html#method-c-new)
i += 1
end
end
end
这似乎很合理,甚至可以变成Array本身的一个扩展。
module EnumSlice
def enum_slice(i, j)
Enumerator.new do |y|
while i <= j
y << self[i]
i += 1
end
end
end
end
现在在数组中 Enumerator
块。y
代表 Proc
当你有更多数据时,你就会调用它。如果该块结束,则假定你已经完成了枚举。没有要求永远终止,一个无限的枚举器是允许的,在这种情况下,由调用者决定是否停止迭代。
所以换句话说, y
块参数可以调用 零次以上,每次调用它的时候,输出都会从枚举器中 "发射 "出来。当该块退出时,枚举器被认为是完成了,并被关闭。y
在这一点上是无效的。
所有 y << x
是调用 <<
办法 Enumerator::Yielder
,这是一种语法上的糖分,以避免必须进行 y.call(x)
或 y[x]
,这两个看起来都有点丑。
现在你可以把它添加到Array中。
Array.include(EnumSlice)
现在你可以做这样的事情
[ 1, 2, 3, 4, 5, 6 ].enum_slice(2, 4).each do |v|
p v
end
给你正确的输出。
值得注意的是,尽管做了这么多工作,但这并不能为你节省任何时间。已经有了内置的方法。你的 enum_slice(a, i, j)
法相当于。
a.drop(i).take(j)
在性能上是否接近?一个快速的基准可以帮助测试这个理论。
require 'benchmark'
Benchmark.bm do |bm|
count = 10000
a = (0..100_000).to_a
bm.report(:enum_slice) do
count.times do
a.enum_slice(50_000, 25_000).each do
end
end
end
bm.report(:drop_take) do
count.times do
a.drop(50_000).take(25_000).each do
end
end
end
end
结果是:
user system total real
enum_slice 0.020536 0.000200 0.020736 ( 0.020751)
drop_take 7.682218 0.019815 7.702033 ( 7.720876)
所以你的方法大约快了374倍 还不错!