2011-04-04 12 views
7

istnieje prostsze, czystsze sposób pisać kod tak:Wiele iteracje

(1..10).each do |i| 
    (1..10).each do |j| 
    (1..10).each do |k| 
     (1..10).each do |l| 
     puts "#{i} #{j} #{k} #{l}" 
     end 
    end 
    end 
end 

Idealnie byłbym w stanie zrobić coś takiego ...

(1..10).magic(4) { |i, j, k, l| puts "#{i} #{j} #{k} #{l}" } 

lub nawet lepiej ..

magic(10, 4) { |i, j, k, l| puts "#{i} #{j} #{k} #{l}" } 

Jeśli nie ma czegoś wbudowanego, w jaki sposób mogę napisać metodę taką jak ostatnia?

+0

co wyjście można się spodziewać? czy jest to coś w rodzaju '1 2 3 4 \ n 5 6 7 8 \ n 9 10' lub' 0 0 0 1 \ n 0 0 0 2 ... '? –

+1

@nash Nie; pierwszy kod działa samodzielnie. Jeśli dopasowałeś wszystkie '(1..10)' do '(0..9)', wtedy otrzymasz '0 0 0 0 ',' 0 0 0 1 ', ...' 9 9 9 8 ', "9 9 9 9". – Phrogz

+0

To jest duplikat http://stackoverflow.com/questions/5226895/combine-array-of-array-into-all-possible-combinations-forward-only-in-ruby/5227021#5227021, ale lubię to odpowiedzi na pytania lepiej. –

Odpowiedz

9

Jeśli jesteś na Ruby 1.9, można to zrobić:

range = (1..10).to_a 
range.repeated_permutation(4) do |set| 
    puts set.join(" ") 
end 

W Ruby 1.8: rozwiązanie

range = (1..10).to_a 
range.product(range, range, range).each do |set| 
    puts set.join(" ") 
end 
+0

Lub nawet 'range.product ([zakres] * 3) .each ...' – Phrogz

2

dmarkow za (wierzę) materializuje zakresy, które przynajmniej w teorii , zużywa więcej pamięci niż potrzebujesz. Oto sposób to zrobić bez materializacji zakresy:

def magic(ranges, &block) 
    magic_ = lambda do |ranges, args, pos, block| 
    if pos == ranges.length 
     block.call(*args) 
    else 
     ranges[pos].each do |i| 
     args[pos] = i 
     magic_.call(ranges, args, pos+1, block) 
     end 
    end 
    end 
    magic_.call(ranges, [nil]*ranges.length, 0, block) 
end 

magic([1..10] * 4) do |a,b,c,d| 
    puts [a, b, c, d].inspect 
end 

Powiedział, że wydajność jest taka prosta sprawa i nie wiem, jak skuteczne Ruby jest z wywołań funkcji, więc może trzymanie się funkcji bibliotecznych jest najszybszym sposobem iść.

Aktualizacja: Took sugestię Phrogz”i umieścić magic_ wewnątrz magic. (Aktualizacja: Ponownie zasugerowałem sugestię Phrogza i miejmy nadzieję, że zrobiło się dobrze tym razem z lambda zamiast def).

Aktualizacja: Array#product zwraca Array, więc jestem przy założeniu, że jest w pełni zrealizowana. Nie mam Ruby 1.9.2, ale Mladen Jablanović zauważył, że Array#repeated_permutation prawdopodobnie nie zmaterializowało całości (nawet jeśli początkowy zasięg zmaterializował się na to_a).

+0

'repeat_permutation' nie powinien zmaterializować całej tablicy, gdy jest wywoływany bez bloku, ale zamiast tego jest to moduł wyliczający, który możesz następnie przemierzać używając' each', bez tworzenia duża struktura w pamięci (miejmy nadzieję). –

+0

Prawie napisałem ten formularz, ale rozleniwiłem się, myśląc o rekursji. :) Zauważ jednak, że z czymś takim jestem zwolennikiem stworzenia lokalnej 'magic_' lambda wewnątrz twojej' magicznej' metody i pozwalającej jej rekurencyjnie nazywać siebie. Dzięki temu nie istnieje zanieczyszczenie przestrzeni nazw dla niepożądanej dodatkowej metody. +1 dla bardzo ogólnego rozwiązania. – Phrogz

+0

Rozumiem, że wziąłeś moją sugestię, ale to, co zrobiłeś, nie zagnieździło funkcji. Definiuje nową zewnętrzną funkcję 'magic_' za każdym razem, gdy uruchomisz metodę' magic'! Zamiast tego sugerowałem: "def magia (...); magic_ = lambda {| r, a, p, b | ... magia_[ ... ] }; magia_[ ... ]; end' – Phrogz

2

Wziąłem wolność zmieniając kolejność parametrów magic przy założeniu, że podstawy 10 jest bardziej powszechne i opcjonalnie:

def magic(digits,base=10) 
    raise "Max magic base of 36" unless base <= 36 
    (base**digits).times do |i| 
    str = "%#{digits}s" % i.to_s(base) 
    parts = str.scan(/./).map{ |n| n.to_i(base)+1 } 
    yield *parts 
    end 
end 

magic(3,2){ |a,b,c| p [a,b,c] } 
#=> [1, 1, 1] 
#=> [1, 1, 2] 
#=> [1, 2, 1] 
#=> [1, 2, 2] 
#=> [2, 1, 1] 
#=> [2, 1, 2] 
#=> [2, 2, 1] 
#=> [2, 2, 2] 

magic(2,16){ |a,b| p [a,b] } 
#=> [1, 1] 
#=> [1, 2] 
#=> [1, 3] 
#=> ... 
#=> [16, 15] 
#=> [16, 16] 

Wyjaśnienie:

Przekładając pierwotnego problemu od 1..10 do 0..9 i łączenie cyfr widzimy, że dane wyjściowe właśnie się liczy, z dostępem do każdej cyfry.

0000 
0001 
0002 
... 
0010 
0011 
0012 
... 
9997 
9998 
9999

Tak właśnie robi mój powyższy kod.Liczy od 0 do liczby maksymalnej (na podstawie liczby cyfr i dopuszczalne wartości za cyfra), a dla każdego numeru IT:

  1. Konwertuje liczbę do odpowiedniej „bazy”:
    i.to_s(base)            # e.g. 9.to_s(8) => "11", 11.to_s(16) => "b"

  2. Używa String#% pad ciąg do prawidłowego liczba znaków:
    "%#{digits}s" % ...     # e.g. "%4s" % "5" => "   5"

  3. zamienia ten pojedynczy łańcuch na tablicę pojedynczy ciąg znaków s:
    str.scan(/./)           # e.g. " 31".scan(/./) => [" ","3","1"]
    Należy zauważyć, że w Ruby 1.9 to lepiej zrobić z str.chars

  4. Przekształca każde z tych ciągów pojedynczych znaków z powrotem na numer:
    n.to_i(base)            # e.g. "b".to_i(16) => 11, " ".to_i(3) => 0

  5. dodaje 1 do siebie tych liczb, ponieważ pożądane było rozpoczęcie od 1 zamiast 0

  6. Ułożenie nowej tablicy liczb jako argumentów t O bloku, numer jeden na blok param:
    yield *parts

+0

To jest naprawdę fajne. Czy mógłbyś wyjaśnić linie 'str =' i 'parts ='? Mam trudności z ich przestrzeganiem. – Drew

+0

@ Drew Aktualizuję odpowiedź z wyjaśnieniem, jak to działa. – Phrogz

+0

Teraz rozumiem, dziękuję. :) – Drew

Powiązane problemy