是否有更简单,更清晰的方式来编写这样的代码:
(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 :(得分: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
答案 1 :(得分: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
实现的。)
答案 2 :(得分:2)
我假设基础10更常见且可选,我冒昧地改变了magic
参数的顺序:
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..10
转换为0..9
并连接数字,我们看到输出只是计数,可以访问每个数字。
0000 0001 0002 ... 0010 0011 0012 ... 9997 9998 9999
这就是我上面的代码所做的。它从0到最大数量(基于每位数字的位数和允许值),以及每个数字:
将数字转换为适当的“基数”:
i.to_s(base) # e.g. 9.to_s(8) => "11", 11.to_s(16) => "b"
使用String#%
将字符串填充到正确的字符数:
"%#{digits}s" % ... # e.g. "%4s" % "5" => " 5"
将此单个字符串转换为单字符字符串数组:
str.scan(/./) # e.g. " 31".scan(/./) => [" ","3","1"]
请注意,在Ruby 1.9中,最好使用str.chars
将每个单字符字符串转换回数字:
n.to_i(base) # e.g. "b".to_i(16) => 11, " ".to_i(3) => 0
为这些数字中的每一个添加1,因为希望从1开始而不是0
将这个新的数字数组作为块的参数,每个块参数一个数字:
yield *parts