2012-07-05 10 views
6
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) 

Soy nuevo en ruby. Y entiendo cómo definir un método de instancia de Producto cartesiano, pero no tengo idea de esto. ¿Cómo debería construir el objeto de clase para cumplir el requisito?Producto cartesiano Ruby

+0

¿Puede aclarar lo que está pidiendo? ¿Cómo deberías construir qué? ¿Estás tratando de crear una clase llamada 'CartesianProduct' que haría lo que se muestra? – denniss

+0

Sí, requiere un método de clase. Sé cómo construir un método de instancia para devolver un valor, pero no sé cómo construir el método de clase para modificar el valor de un objeto de clase. – ZhijieWang

+0

¿Es esta tarea? Si es así, está bien, la gente intentará empujarte en la dirección correcta. – steenslag

Respuesta

6

yo no usaría una clase para eso, pero manteniendo la estructura de la pregunta, me gustaría escribir:

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 

En cambio, yo simplemente escribo xs.product(ys) o construir mi propia Array#lazy_product si fuera la pereza importante (vea esto ticket).

+0

No hay necesidad de usar perezoso, ¿verdad? Directamente 'each' y 'yield' serán más simples y más rápidos –

+0

bien, tenías razón, aunque para ser sincero prefería la versión perezosa, es un enfoque más funcional (y devolvió un enumerador si se usa sin bloque, como un estándar 'cada'). – tokland

+0

De hecho, probablemente debería comenzar con el típico 'return to_enum a menos que block_given?' –

22

Sugiero usar Array#product.

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

Lo que producirá la salida que desee.

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

Si usted quiere un generador perezoso de permutaciones, he escrito algo como esto antes. Pero te advierto, si tienes una gran cantidad de permutaciones para calcular, puede llevar un tiempo. Debería poder tomar lo que necesita de las primeras 40 a 45 líneas de this file (este archivo fue un experimento de todos modos).

El truco consiste en crear enumeradores con Ruby 1.9.2 para abrirse camino en una matriz de matrices. Por lo tanto, primero crea un enumerador que recorrerá un ciclo interminable, y en su enumerador array-of-array rastrea el primer conjunto de resultados y termina el ciclo cuando se golpea por segunda vez. Esta era la única forma en que podía descubrir cómo terminar ese ciclo.

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 } 
+0

¿qué pasa con dos matrices vacías? ¿Cuál será el resultado? – ZhijieWang

+1

@ user1505108 ¿has probado IRB antes? El resultado es '[]'. –

6

es necesario definir un método each en su clase que llama yield para cada combinación del producto.

Puede usar Array#product, pero devuelve una matriz, por lo que no es floja.

Hay un proposal for Array.product en Ruby 2.0 que haría exactamente eso.

+0

Gracias, eso resuelve el problema – ZhijieWang

+0

@ user1505108 Luego debe marcar esta respuesta (el signo v). – steenslag

Cuestiones relacionadas