2012-07-05 15 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) 

Jestem nowy w ruby. Rozumiem, jak zdefiniować metodę instancji produktu kartezjańskiego, ale nie mam o tym pojęcia. Jak utworzyć obiekt klasy, aby spełnić wymagania.Produkt kartezjański Ruby

+0

Czy możesz wyjaśnić, o co prosisz? Jak powinieneś konstruować co? Czy próbujesz utworzyć klasę o nazwie "CartesianProduct", która zrobiłaby to, co pokazano? – denniss

+0

Tak, wymaga to metody klasy. Wiem, jak skonstruować metodę instancji, aby zwrócić wartość, ale nie wiem, jak skonstruować metodę klasy, aby zmodyfikować wartość obiektu klasy. – ZhijieWang

+0

Czy to zadanie domowe? Jeśli tak, to jest w porządku, ludzie będą próbować popchnąć cię we właściwym kierunku. – steenslag

Odpowiedz

6

Nie chciałbym użyć klasy za to, ale zachowując strukturę pytanie, chciałbym napisać:

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 

Zamiast tego, bym po prostu napisać xs.product(ys) lub zbudować własną Array#lazy_product jeśli lenistwo były ważne (patrz: ticket).

+0

Nie ma potrzeby używania leniwych, prawda? Prosty "każdy" i "zysk" będzie prostszy i szybszy, –

+0

ok, miałeś rację, choć szczerze mówiąc wolałem leniwą wersję, jest to bardziej funkcjonalne podejście (i zwrócił moduł wyliczający, jeśli jest używany bez bloku, jak standardowy "każdy"). – tokland

+0

Rzeczywiście, prawdopodobnie powinieneś zacząć od typowego 'return to_enum until block_given?' –

22

Proponuję użyć Array#product.

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

Co przyniesie pożądane wyniki.

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

Jeśli chcesz leniwego generatora permutacji, napisałem coś takiego wcześniej. Ale ostrzegam, jeśli masz dużą liczbę permutacji do obliczenia, może to trochę potrwać. Powinieneś być w stanie wziąć to, czego potrzebujesz od pierwszych 40 - 45 linii this file (ten plik i tak był eksperymentem).

Sztuką jest zbudowanie modułów wyliczających przy użyciu Ruby 1.9.2, aby przejść przez szereg tablic. Najpierw zbudujesz moduł wyliczający, który będzie cyklicznie przechodził przez macierz, aw module wyliczającym tablicę będziesz śledził pierwszy zestaw wyjściowy i zakończy pętlę, gdy zostanie trafiony po raz drugi. To był jedyny sposób, w jaki mogłem wymyślić, jak zakończyć taką pętlę.

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

co z dwoma pustymi tablicami? Jaki będzie wynik? – ZhijieWang

+1

@ user1505108 Czy próbowałeś już wcześniej IRB? Wynikiem jest '[]'. –

6

Musisz określić metodę each w swojej klasie, który wywołuje yield dla każdej kombinacji produktu.

Można użyć Array#product, ale zwraca tablicę, więc nie jest leniwy.

Jest proposal for Array.product w Ruby 2.0, który by to zrobił.

+0

Dziękujemy, że rozwiązuje problem – ZhijieWang

+0

@ user1505108 Następnie należy oznaczyć tę odpowiedź (znak v). – steenslag

Powiązane problemy