让我试一试。
def map(f, lst)
lst.map{|v| f.call(v)}
end
map 接受一个数组lst 并在lst 的每个元素上应用一个函数f。
以下示例将数组的每个元素加倍。
irb(main):007:0> map(lambda {|i| i * 2}, [1,2,3])
=> [2, 4, 6]
接下来,
def fold(f, init, lst)
lst.reduce(init){|w, a| f.call(w, a)}
end
fold 接受一个数组lst,一个初始值init,对lst 的每个元素应用函数f,并将其与之前的结果组合以返回一个值。 f.call(w, a) 的结果存储在 w 中。 w 的初始值为 init。
在纯红宝石中,
w = init
lst.each do |a|
w = f.call(w, a)
end
return w
在以下示例中,fold 通过添加每个元素来获取数组的总和。
irb(main):010:0> fold(lambda {|a, i| a + i}, 0, [1,2,3])
=> 6
现在,主要功能,
def permutations(lst)
lst.empty? ? [[]] : fold(lambda{|w, a| w + a}, [], map(lambda{|v| map(lambda{|p| [v]+p}, permutations(lst-[v]))}, lst))
end
第一部分很简单。如果lst 为空,则返回[[]]。
第二部分是
fold(lambda{|w, a| w + a}, [], map(lambda{|v| map(lambda{|p| [v]+p}, permutations(lst-[v]))}, lst))
我们会从最里面往回走。
map(lambda{|p| [v]+p}, permutations(lst-[v]))
这很简单。对于permutations 返回的每个元素,我们附加[v](v 在外部映射中定义)。 permutations 将数组减去元素 v。
这是一个例子。
irb(main):012:0> permutations([1,2,3]-[3])
=> [[1, 2], [2, 1]]
irb(main):013:0> map(lambda{|p| [3]+p}, permutations([1,2,3] - [3]))
=> [[3, 1, 2], [3, 2, 1]]
接下来我们更进一步,我们看到内部映射中的v 是来自lst 的每个元素。
map(lambda{|v| map( ... )}, lst)
在纯红宝石中,这基本上是
lst.map do |v|
a = permutations(lst - [v])
a.map {|p| [v] + p}
end
终于有了
fold(lambda{|w, a| w + a}, [], map( ... ))
对于map 返回的数组中的每个元素,我们将其添加到w,它最初是一个空数组[]。
irb(main):019:0> fold(lambda {|w, a| w + a}, [], [[[1, 2]], [[2, 1]]])
=> [[1, 2], [2, 1]]
如果您想知道此算法为何有效,请考虑模式。
irb(main):021:0> permutations([1,2,3])
=> [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
与
相同
irb(main):023:0> permutations([1,2]).map {|i| i + [3]}
=> [[1, 2, 3], [2, 1, 3]]
irb(main):024:0> permutations([1,3]).map {|i| i + [2]}
=> [[1, 3, 2], [3, 1, 2]]
irb(main):025:0> permutations([2,3]).map {|i| i + [1]}
=> [[2, 3, 1], [3, 2, 1]]