在Ruby中将生成多个Enumerables的循环转换为单个lazy Enumerable(或Enumerator?),可能使用flat_map / flatten

时间:2015-04-08 17:26:17

标签: ruby yield enumerable flatten

我正在使用包含以下代码的gem,允许用户在Web服务查询返回的每条记录上调用一个块,即使该Web服务查询被分页并需要多个请求:

  def each(&block)
    yield_from_response &block
    while resumable?
      @response = @resumption_block.call @response
      yield_from_response &block
    end
  end

  private

  def yield_from_response(&block)
    @response.each do |obj|
      block.call(obj)
    end
  end

  def resumable?
    @response.resumption_token and not @response.resumption_token.empty?
  end

(对于这个问题,细节不是很重要,但对于那些关心的人来说,宝石是ruby-oai,而且有问题的代码在resumable.rb。)

我正在尝试将each()包装在一个方法中,该方法在响应中的每个对象周围返回Enumerable个包装器对象。如果我不关心恢复,我可以避免上面的代码,只是直接访问响应(这是一个Enumerable):

response ? response.map { |obj| MyGem::MyWrapper.new(obj) } : []

现在,我很容易通过该块:

response ? response.full.each { |obj| MyGem::MyWrapper.new(obj) } : []
# full() provides the resumable wrapper with each() and yield_from_response()

但在恢复案例中,由于each没有返回任何有用的内容,因此无法获得结果。

结果集可能非常大(大于物理RAM大),所以我不只是想明确地或隐式地将所有内容转储到临时数组中。

显然,我需要的是编写自己的each()替代方案而不是阻止,只返回Enumerableobjs嵌入的@responses }。我觉得在没有编写完整的Enumerable实现的情况下应该有一个简单的方法来做到这一点,但我对Ruby来说已经足够新了,它还不清楚它是什么。

我能想到的最好的是以下内容,这也是错误的:

  def to_enumerable(response)
    response.each { |obj| yield MyGem::MyWrapper.new(obj) }
    resumption_block = response.resumption_block
    if resumption_block
      while response.resumption_token && !response.resumption_token.empty?
        response = resumption_block.call response
        response.each { |obj| yield MyGem::MyWrapper.new(obj) }
      end
    end
  end

这是不正确的,因为当然,Ruby的yield并没有真正“屈服”,它只是调用一个块,如果我想传入一个块,我可以调用原来的each()

如果我能弄清楚如何将重复的作业转换为response的{​​{1}}序列,我可以使用responses,但我不知道该怎么做。


ETA:在仔细检查时看起来flat_map不是懒惰的,如果我想避免创建数组(从而耗尽大数据集的内存)我可能实际上想要Enumerable。如果我理解正确的区别。

0 个答案:

没有答案