8
class CartesianProduct
include Enumerable
# your code here
end
#Examples of use
c = CartesianProduct.new([:a,:b], [4,5])
c.each { |elt| puts elt.inspect }
# [:a, 4]
# [:a, 5]
# [:b, 4]
# [:b, 5]
c = CartesianProduct.new([:a,:b], [])
c.each { |elt| puts elt.inspect }
# (nothing printed since Cartesian product
# of anything with an empty collection is empty)

我是红宝石新手。而且我了解如何定义笛卡尔积的实例方法,但对此我一无所知。我应该如何构造类对象来满足要求。

4

3 回答 3

29

我建议使用Array#product.

[:a, :b].product [4,5]

这将产生你想要的输出。

irb(main):001:0> [:a, :b].product [4,5]
=> [[:a, 4], [:a, 5], [:b, 4], [:b, 5]]
irb(main):002:0> 

如果你想要一个惰性的排列生成器,我以前写过这样的东西。但是我警告你,如果你有大量的排列来计算它可能需要一段时间。您应该能够从该文件的前 40 - 45 行中获取您需要的内容无论如何,该文件是一个实验)。

诀窍是使用 Ruby 1.9.2 构建枚举器来处理数组数组。因此,您首先构建一个将在数组中无限循环的枚举器,然后在您的数组数组枚举器中跟踪第一个输出集并在第二次命中时结束循环。这是我能弄清楚如何终止这样一个循环的唯一方法。

def infinite_iterator(array)
  Enumerator.new do |result|
    loop do
      array.cycle { |item| result << item }
    end
  end
end

def cartesian_iterator(data)
  Enumerator.new do |result|
    first = data.map { |p| p.next }
    result << first

    i = 1
    parts = first.dup
    loop do
      parts[2-i] = data[2-i].next
      break if parts == first

      result << parts.join
      i = ((i + 1) % parts.size)
    end
  end
end

array = [ infinite_iterator([:a,:b]), infinite_iterator([4,5]) ]
generator = cartesian_iterator(array)

generator.each { |a| p a }
于 2012-07-05T20:36:47.820 回答
8

I wouldn't use a class for that, but keeping the structure of the question, I'd write:

class CartesianProduct
  include Enumerable

  def initialize(xs, ys)
    @xs = xs
    @ys = ys
  end

  def each
    return to_enum unless block_given?
    @xs.each do |x| 
      @ys.each { |y| yield [x, y] }
    end
  end
end

Instead, I'd simply write xs.product(ys) or build my own Array#lazy_product if laziness were important (see this ticket).

于 2012-07-05T21:22:36.233 回答
7

您需要each在您的类中定义一个方法来调用yield产品的每个组合。

您可以使用Array#product,但它返回一个数组,因此它并不懒惰。

Ruby 2.0 中有一个提案Array.product可以做到这一点。

于 2012-07-05T20:36:57.440 回答