2011-04-04 79 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