如何简化这个Enumerator代码?

6
我希望能优化以下代码,使其更加简洁。
x1.each { |x| 
  x2.each { |y|
    ....
    xN.each { |z|
      yield {}.merge(x).merge(y)...... merge(z)
    }
  }
}

假设x1, x2, ..., xN枚举器对象。

  1. 上述内容不够简洁明了。
  2. 它适用于作为Array的x1、x2,但不适用于作为Enumerator的x1、x2。
    • 因为枚举器迭代器应该在内部循环中重置。

我尝试了这个但没有成功:

[x1, x2, ..., xN].reduce(:product).map { |x| x.reduce :merge }

您有什么建议吗?

更新

目前解决方案为:

[x1, x2, ..., xN].map(:to_a).reduce(:product).map { |x| 
  yield x.flatten.reduce(:merge) 
}

6
优化是指性能方面的还是简洁性方面的? - Frederick Cheung
1
x1.product(x2,.,xn).each { |hash,elem| elem.reduce({},:merge) } 可能有效... - Arup Rakshit
  1. 简洁性
  2. 这个代码可以与x1、x2作为数组一起使用,但是不能与枚举器一起使用,因为对于内部循环,枚举器迭代器应该被重置。我尝试过这样做,但没有成功:
[x1, x2, ..., xN].reduce(:product).map { |x| x.reduce :merge }
- Alex
1
@Arup Rakshit:在Enumerator实例上未定义product - Neil Slater
@ArupRakshit - 你需要先调用 to_a 方法,但这并不适用于所有枚举器。 - BroiSatse
1个回答

5
我将从第二点开始:
至少在我测试过的枚举器([{a: 1},{a: 2},{a: 3}]。each)中,你的代码可以工作 - 显然枚举器#each要么在结尾处重新定位,要么使用自己的指针。
要做你想做的事情,你需要迭代枚举器对象(特别是内部对象)多次,以至于首先在每个对象上调用to_a不会增加你的时间复杂度(它将保持O(n1*n2*...*nk)。
关于第一点,如果调用to_a不可行,你可以考虑递归:
def deep_merge(enum = nil, *enums)
  if enum.nil?
    yield({})
  else
    enum.each do |x|
      deep_merge(*enums) do |h|
        yield h.merge(x)
      end
    end
  end
end

现在您可以调用deep_merge(x1, x2, ... xN)并获得所需的结果...

1
即使 OP 可以使用 .to_a 进行操作,我认为任何避免创建巨大产品的结构(就像这个答案一样)都是一个好的结果。你可以相对快速地迭代 1000 万个项目,但是为了这样做而在内存中保存 1000 万个哈希可能会影响性能。 - Neil Slater

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接