2011-04-04 496 views
7

有沒有寫這樣的代碼更簡單,更清潔的方式:多次迭代

(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 

理想我可以做這樣的事情......

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

甚至更​​好.. 。

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

如果有沒有內置的東西,怎麼會寫我喜歡最後一個方法?

+0

你希望輸出什麼?是否像'1 2 3 4 \ n 5 6 7 8 \ n 9 10'或'0 0 0 1 \ n 0 0 0 2 ...'? – 2011-04-04 20:49:55

+1

@nash否;第一個代碼自行運行。如果你將所有'(1..10)'調整爲'(0..9)',那麼你就會得到'0 0 0 0','0 0 0 1',...'9 9 9 8', '9 9 9 9''。 – Phrogz 2011-04-04 21:03:31

+0

這是http://stackoverflow.com/questions/5226895/combine-array-of-array-into-all-possible-combinations-forward-only-in-ruby/5227021#5227021的副本,但我喜歡這個問題的答案更好。 – 2011-04-05 04:50:34

回答

9

如果你在Ruby 1.9的,你可以這樣做:

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

在Ruby 1.8:

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

甚至'range.product([range] * 3)。每個...' – Phrogz 2012-02-25 14:52:36

2

dmarkow的解決方案(我相信)物化的範圍,並且至少在理論上,使用更多的內存比你需要。這裏有一個辦法做到這一點,而不將範圍:

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 

這就是說,性能是一個棘手的事情,我不知道Ruby的效率如何與函數調用,所以也許堅持的庫函數是最快的方法去。

更新:採取了Phrogz的建議,並把magic_magic。 (更新:採取了Phrogz的建議再次,並希望這次與lambda而不是def正確)。

更新Array#product返回Array,所以我假設完全物化。我沒有Ruby 1.9.2,但是MladenJablanović指出Array#repeated_permutation可能不會實現整個事情(儘管最初的範圍是to_a)。

+0

'repeat_permutation'不應該在沒有阻塞的情況下調用整個數組,而是使用Enumerator代替,然後您可以使用'each'遍歷而不創建記憶中的大結構(希望)。 – 2011-04-04 21:12:58

+0

我幾乎寫了這個表單,但在思考遞歸時變得很懶惰。 :)但是,請注意,通過這樣的事情,我主張在'magic'方法內創建一個本地'magic_' lambda,並讓它遞歸調用它自己。有了這個,對於不必要的額外方法就沒有命名空間污染。然而,對於非常普遍的解決方案,+1 – Phrogz 2011-04-04 21:16:51

+0

我很欣賞你採納了我的建議,但是你所做的不能嵌套這些功能。它每次運行魔術方法時都會定義一個新的外部'magic_'函數!相反,我暗示:'def魔法(...); magic_ = lambda {| r,a,p,b | ... 魔法_[ ... ] };魔法_[ ... ];結束' – Phrogz 2011-04-05 21:48:28

2

我已經採取改變你magic參數的假設下,該基地10是比較常見的和可選順序的自由:

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] 

說明

通過翻譯原來的問題從1..100..9並連接數字,我們看到輸出正在計數,並訪問每個數字。

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

這就是我上面的代碼所做的。它從0計數到(基於數字的數目並且允許每數位值)的最大數量,併爲每個數它:

  1. 的數字轉換成適當的「基礎」:
    i.to_s(base)            # e.g. 9.to_s(8) => "11", 11.to_s(16) => "b"

  2. String#%用途到墊字符串到正確的字符數:
    "%#{digits}s" % ...     # e.g. "%4s" % "5" => "   5"

  3. 打開此單字符串轉換成單個字符的字符串的數組S:
    str.scan(/./)           # e.g. " 31".scan(/./) => [" ","3","1"]
    注意,在Ruby 1.9的,這是更好地與str.chars

  4. 完成轉換每個單字符的字符串返回到一個號碼:
    n.to_i(base)            # e.g. "b".to_i(16) => 11, " ".to_i(3) => 0

  5. 增加1到每個這些數字,因爲願望是從1開始而不是0

  6. 將這個新的數組數組作爲參數Ø塊,每塊的PARAM一個數字:
    yield *parts

+0

這真的很酷。你能解釋一下'str ='和'parts ='行嗎?我很難跟隨他們。 – Drew 2011-04-05 21:04:14

+0

@Drew我已經更新了答案,並解釋了它的工作原理。 – Phrogz 2011-04-05 21:21:51

+0

現在我明白了,謝謝。 :) – Drew 2011-04-05 21:30:14