【问题标题】:How does this recursive functional permutation generator work?这个递归函数置换生成器是如何工作的?
【发布时间】:2020-12-01 11:58:40
【问题描述】:

此代码以函数式编程风格编写。 以下是它的工作原理。

(permutations ‘(a b c))  -->  ((a b c) (a c b) (b a c) (b c a) (c a b) (c b a))

请解释foldmap 在这段代码中的工作原理以及递归排列...

def map(f, lst)
  lst.map{|v| f.call(v)}
end

def fold(f, init, lst)
  lst.reduce(init){|w, a| f.call(w, a)}
end

def permutations(lst)
  lst.empty? ? [[]] : fold(lambda{|w, a| w + a}, [], map(lambda{|v| map(lambda{|p| [v]+p}, permutations(lst-[v]))}, lst))
end

【问题讨论】:

  • 你到底卡在哪里了?由于这看起来像是一个家庭作业问题,因此您可能已经知道大部分代码已经做了什么。
  • foldmap 是现有方法还是您应该实现它们?
  • 最初我以为你正在构造一个方法Array#permutations 来模仿核心方法Array#permutation,但事实上permutations 接受的参数可能是一个数组而不是一个整数放这个想法休息。 fold 方法也是一个谜,因为 Ruby 没有该名称的核心方法。请澄清您的问题。
  • ohhhh 抱歉,'map' 和 'fold' 不是现有方法。我编辑了代码。请再看一遍。我一直在解释代码。

标签: ruby lambda functional-programming


【解决方案1】:

让我试一试。

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]]

【讨论】:

  • omg 非常感谢? 一切都清楚地解释了它是如何工作的..
  • 嗯,除了一件事。 'irb(main):019:0> fold(lambda {|w, a| w + a}, [], [[[1, 2]], [[2, 1]]]) => [[1 , 2], [2, 1]]' 这部分,怎么加了 [[[1, 2]], [[2, 1]]] 变成了 [[1, 2], [2, 1]] [[[1, 2]], [[2, 1]]] 到 w?
  • @SooIn [[[1,2]], [[2,1]]] 中的每个元素是 [[1,2]] 和 [[2, 1]]。所以你在做 [] + [[1,2]] + [[2,1]] = [[1,2], [2,1]]
【解决方案2】:

请解释foldmap 在此代码中的工作原理

这些只是消息发送(在其他语言中它们被称为方法调用)。它们分别调用了名为foldmap 的方法。

还有递归排列...

permutation 方法使用名为foldmap 的方法。这些方法没有定义,所以这只是raise a NoMethodError

【讨论】:

  • fold 和 map 不是现有方法。我重新定义了它们。
猜你喜欢
  • 2016-11-01
  • 2016-12-18
  • 2011-03-29
  • 2016-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-14
  • 1970-01-01
相关资源
最近更新 更多