我正在尝试使用Ruby 1.9来处理一个枚举器的Lazy Evaluation。这是正在进行的工作,所以可能会有其他错误/缺少代码,但我现在有一个特定的问题。我正试图通过这个测试(注意我不能改变测试):
def test_enumerating_with_a_single_enumerator
enumerator = SomeClass.new(some_infinite_sequence.to_enum)
assert_equal [1, 2, 3, 4, 5], enumerator.take(5)
end
我在下面编写了这段代码,我知道问题是我从 initialize 方法的参数中调用SomeClass中的 lazy_select 实例方法是Enumerator类的一个实例,所以我得到一个NoMethodError。有什么建议?谢谢。
class SomeClass < Enumerator
def initialize(*enumerators)
super() do |yielder|
enumerators.each do |enumerator|
enumerator.lazy_select { |yielder, first_value, second_value| yielder.yield first_value if (first_value <=> second_value) <= 0 }
.first(20)
end
end
end
def lazy_select(&block)
self.class.new do |yielder|
each_cons(2) do |first_value, second_value|
block.call(yielder, first_value, second_value)
end
end
end
end
答案 0 :(得分:0)
我现在有一个具体问题。我正试图通过这个测试 (注意我不能改变测试):
def test_enumerating_with_a_single_enumerator enumerator = SomeClass.new(some_infinite_sequence.to_enum) assert_equal [1, 2, 3, 4, 5], enumerator.take(5) end
class SomeClass < Enumerator
def initialize(enum, &block)
super() do |y|
begin
enum.each do |val|
if block
block.call(y, val) #while initializing sc2 in Line B execution takes this branch
else
y << val #while initializing sc1 from Line A execution halts here
end
end
rescue StopIteration
end
end
end
def lazy_take(n)
taken = 0
SomeClass.new(self) do |y, val| #Line B
if taken < n
y << val
taken += 1
else
raise StopIteration
end
end
end
def take(n)
lazy_take(n).to_a
end
end
sc1 = SomeClass.new( (1..6).cycle ) #Line A
p sc1.take(10)
--output:--
[1, 2, 3, 4, 5, 6, 1, 2, 3, 4]
sc2是我给lazy_take()内部创建的匿名实例的名称。
代码很难理解。代码设置为sc1的枚举器为循环,sc2的枚举器为sc1(initialize()要求第一个arg为枚举器)。初始化sc1时,代码开始逐步执行循环中的值并在此行停止:
y << val
然后,当调用lazy_take()时,会创建sc2,并且其初始化代码开始单步执行sc1中的值。但是sc1中没有值,因此sc1执行以下行:
y << val
将循环中的值注入sc1的yielder。然后sc1的yielder立即将val输出到sc2 - 因为在sc2的代码中,each()方法要求sc1中的值。然后sc2接受val并将其注入sc2的yielder。然后,sc2中每个块的下一次迭代发生,并且sc2的代码再次要求来自sc1的值。 sc2反复要求sc1中的值,这会导致sc1传递从循环中检索到的值。一旦sc2运行循环n次,它就会停止从sc1请求值。下一步是让sc2放弃它的yielder中的值。
如果您愿意,可以像这样定义initialize():
def initialize(enum)
super() do |y|
begin
enum.each do |val|
if block_given?
yield y, val #while initializing sc2 in Line B execution takes this branch
else
y << val #while initializing sc1 from Line A execution halts here
end
end
rescue StopIteration
end
end
end
这表明您不必指定块参数并显式调用()块。相反,您可以省去block参数并调用yield(),这些值将自动发送到块。
答案 1 :(得分:0)
感谢上面收到的评论。他们非常有帮助。我设法按如下方式解决了这个问题:
class SomeClass < Enumerator
class SomeOtherClass < RuntimeError
attr_reader :enumerator
def initialize(enumerator)
@enumerator = enumerator
end
end
def initialize(*enumerators)
super() do |yielder|
values = []
enumerators.each do |enumerator|
values.push lazy_select(enumerator) { |value| sorted? enumerator }.take(@number_to_take)
end
values.flatten.sort.each { |value| yielder.yield value }
end
end
def lazy_select(enumerator, &block)
Enumerator.new do |yielder|
enumerator.each do |value|
yielder.yield value if block.call enumerator
end
end
end
def sorted?(enumerator)
sorted = enumerator.each_cons(2).take(@number_to_take).all? { |value_pair| compare value_pair }
sorted || raise(SomeClass::SomeOtherClass, enumerator)
end
def compare(pair)
pair.first <= pair.last
end
def take(n)
@number_to_take = n
super
end
end
这通过了我的所有测试。