2011-04-04 30 views
7

Có một cách sạch dễ dàng hơn để viết mã như thế này:Nhiều lần lặp

(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 

Lý tưởng nhất là tôi có thể làm điều gì đó giống như ...

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

Hoặc thậm chí tốt hơn .. .

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

Nếu không có thứ gì đó được tích hợp, tôi sẽ viết phương pháp như phương pháp cuối cùng như thế nào?

+0

gì đầu ra nào bạn mong đợi? là một cái gì đó như '1 2 3 4 \ n 5 6 7 8 \ n 9 10' hoặc' 0 0 0 1 \ n 0 0 0 2 ... '? –

+1

@nash No; mã đầu tiên tự chạy. Nếu bạn điều chỉnh tất cả '(1..10)' thành '(0..9)' thì bạn sẽ nhận được ''0 0 0 0', '0 0 0 1', ... '9 9 9 8', '9 9 9 9''. – Phrogz

+0

Đây là bản sao của http://stackoverflow.com/questions/5226895/combine-array-of-array-into-all-possible-combinations-forward-only-in-ruby/5227021#5227021, nhưng tôi thích điều này câu hỏi của câu trả lời tốt hơn. –

Trả lời

9

Nếu bạn đang ở trên của Ruby 1.9, bạn có thể làm điều này:

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

Trong Ruby 1.8: giải pháp

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

Hoặc thậm chí 'range.product ([phạm vi] * 3) .each ...' – Phrogz

2

dmarkow của (Tôi tin) materializes dãy đó, ít nhất là về mặt lý thuyết , sử dụng nhiều bộ nhớ hơn mức bạn cần. Đây là một cách để làm điều đó mà không thể hóa các dãy:

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 

Điều đó nói rằng, hiệu suất là một điều khó khăn và tôi không biết làm thế nào hiệu quả Ruby là với các cuộc gọi chức năng, như vậy có lẽ gắn bó với chức năng thư viện là cách nhanh nhất đi.

Cập nhật: Đã lấy đề xuất của Phrogz và đặt magic_ bên trong magic. (Cập nhật: Đã đề xuất một lần nữa đề nghị của Phrogz và hy vọng đã làm điều đó đúng lúc này với lambda thay vì def).

Cập nhật: Array#product trả về một Array, vì vậy tôi giả định rằng nó được thực hiện đầy đủ. Tôi không có Ruby 1.9.2, nhưng Mladen Jablanović chỉ ra rằng Array#repeated_permutation có lẽ không thực hiện toàn bộ điều (mặc dù phạm vi ban đầu được hiện thực hóa với to_a).

+0

'repeat_permutation' không nên thực hiện toàn bộ mảng khi được gọi mà không có khối, mà thay vào đó một Enumerator, sau đó bạn có thể đi qua' each' mà không cần tạo cấu trúc lớn trong bộ nhớ (hy vọng). –

+0

Tôi gần như đã viết mẫu này, nhưng đã lười biếng khi suy nghĩ về đệ quy. :) Lưu ý, tuy nhiên, với một cái gì đó như thế này tôi ủng hộ việc tạo ra một 'magic_' lambda cục bộ bên trong phương thức' ma thuật' của bạn và cho phép nó đệ quy gọi chính nó. Với điều này không có ô nhiễm không gian tên cho một phương pháp không mong muốn thêm. 1 cho một giải pháp rất chung chung, tuy nhiên. – Phrogz

+0

Tôi đánh giá cao rằng bạn đã đề xuất của tôi, nhưng những gì bạn đã làm không làm tổ chức năng. Nó định nghĩa một hàm 'magic_' bên ngoài mới mỗi khi bạn chạy phương thức' ma thuật'! Thay vào đó, tôi đã gợi ý: 'def magic (...); magic_ = lambda {| r, a, p, b | ... ma thuật_[ ... ] }; ma thuật_[ ... ]; end' – Phrogz

2

Tôi đã lấy sự tự do của việc thay đổi thứ tự của magic thông số của bạn theo giả định rằng cơ số 10 là phổ biến hơn và tùy chọn:

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] 

Giải thích:

By dịch vấn đề gốc từ 1..10 đến 0..9 và ghép các chữ số mà chúng tôi thấy rằng đầu ra chỉ đếm, với quyền truy cập vào từng chữ số.

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

Vì vậy, đó là mã của tôi ở trên.Nó đếm từ 0 lên đến số lượng tối đa (dựa trên số lượng các chữ số và phép giá trị mỗi chữ số), và đối với mỗi số đó:

  1. Chuyển đổi số vào thích hợp 'cơ sở':
    i.to_s(base)            # e.g. 9.to_s(8) => "11", 11.to_s(16) => "b"

  2. Sử dụng String#% để pad chuỗi với số đúng của các nhân vật:
    "%#{digits}s" % ...     # e.g. "%4s" % "5" => "   5"

  3. Hóa chuỗi đơn này vào một mảng của chuỗi ký tự duy nhất s:
    str.scan(/./)           # e.g. " 31".scan(/./) => [" ","3","1"]
    Lưu ý rằng trong Ruby 1.9 này được thực hiện tốt hơn với str.chars

  4. Chuyển đổi mỗi một trong các chuỗi ký tự duy nhất trở thành một số:
    n.to_i(base)            # e.g. "b".to_i(16) => 11, " ".to_i(3) => 0

  5. Thêm 1 tới mỗi của những con số này, vì mong muốn bắt đầu từ 1 thay vì 0

  6. Ghép mảng số mới này làm đối số t o khối, một số cho mỗi khối param:
    yield *parts

+0

Điều này thực sự tuyệt vời. Bạn có thể giải thích các dòng 'str =' và 'parts ='? Tôi đang gặp khó khăn sau họ. – Drew

+0

@Drew Tôi đã cập nhật câu trả lời với giải thích về cách hoạt động của nó. – Phrogz

+0

Bây giờ tôi hiểu, cảm ơn. :) – Drew

Các vấn đề liên quan