1

我正在尝试使用 Ruby 1.9 掌握对枚举器的惰性求值。这是正在进行的工作,因此可能会有其他错误/缺少代码,但我现在有一个具体问题。我正在尝试通过此测试(注意我无法更改测试):

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

我在下面编写了这段代码,我知道问题是我正在从作为 Enumerator 类的实例的初始化方法的参数上调用 SomeClass 中的lazy_select实例方法,所以我得到了 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
4

2 回答 2

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

这通过了我所有的测试。

于 2013-08-09T18:35:06.050 回答
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

这表明您不必指定块参数并显式调用()该块。相反,您可以省去块参数并调用 yield(),值将自动发送到块。

于 2013-08-08T18:11:08.233 回答