module IntegerExt
# Define a refinement of the Integer type. This is like
# monkeypatching but is local to modules or files that opt in to the
# change.
refine Integer do
# Re-write the times function.
def times(&block)
if block
# If a block was provided, then do the "normal" thing.
super(&block)
else
# Otherwise, get an enumerator and augment its singleton class
# with PerSecondEnumerator.
super().extend PerSecondEnumerator
end
end
end
# This module provides the per_second method on enumerators that
# need it.
module PerSecondEnumerator
def per_second(&block)
loop do
sleep(1.0/self.size)
block.call
end
end
end
end
# If a module wishes to have this functionality, it needs to opt in
# with this 'using' statement. The per_second syntax only works on
# objects constructed within the lexical scope of this refinement.
using IntegerExt
puts 2.times.inspect # Still works
2.times.per_second { p 'works' }
obj = Object.new
def obj.each_second(times: 1, duration: Float::INFINITY)
return to_enum(__method__, times: times, duration: duration) unless block_given?
i = 0
until i >= duration * times do
i += 1
yield(i)
sleep(1.0/times)
end
end
enum = obj.each_second(times: 5, duration: 3)
enum.each { |n| puts "#{n} Works"}
# 1 Works
# 2 Works
# 3 Works
# 4 Works
# 5 Works
# 6 Works
# 7 Works
# 8 Works
# 9 Works
# 10 Works
# 11 Works
# 12 Works
# 13 Works
# 14 Works
# 15 Works
#=> nil
3条答案
按热度按时间mbzjlibv1#
利用现代Ruby的魔力,我们可以编写refinement到
Integer
,以扩展times
方法,但只能在选择这种细化行为的作用域中。这就解决了Aria回答中的几个问题。我们不再对内置类进行全局monkey-patching。我们只在任何模块的词法作用域中修改类,只要该模块 * 希望 * 看到我们的修改。此外,由于我们改进了
times
,而不是直接改进Enumerator
,因此我们的per_second
只适用于通过times
构造的枚举器。所以不可能做像[1, 2, 3, 4, 5].per_second { ... }
这样无意义的事情。8iwquhpp2#
你可以这样做:
但这里有一些警告:
1.不建议扩展Ruby基类,如Enumerator、Integer等。
1.就像有人在你的帖子的评论中说的那样,使用size并不好,因为你可以使用字符串或数组来代替整数。
igetnqfo3#
下面是使用简单对象的另一个选项
您可以指定每秒执行块的次数和执行的持续时间(以秒为单位)。(默认为无限期每秒1次迭代)
注意:这并不考虑块本身的实际阻塞执行时间,因此每秒迭代次数无法保证,可能会小于指定的次数。