Ruby递归lambda调用的问题

时间:2011-03-02 19:18:15

标签: ruby recursion lambda

我有以下代码正确遍历图中的所有节点,如下所示:

seen = {}
dfs = lambda do |node|
  return if seen[node]
  seen[node] = true
  $edges[node].each {|n| dfs.call n}
end
dfs.call 0

但是,我想这样写,我明白这是正确的:

  $edges[node].each &dfs

然而,当我这样做时,似乎只在dfs中的节点列表的第一个元素上调用$edge[node]。是什么给了什么?

2 个答案:

答案 0 :(得分:3)

令人惊讶的是,你的问题在递归中!这实际上是因为seen中所有来电中的共享$nodes[node].each &dfs集合。

让我们进行操作:对$nodes[node].first的调用应该没有任何问题,因为我们知道该代码段适用于任何一个节点。但是有一个问题:seen没有重置,你已经去了下一个节点!您已经看到了所有节点,因此当您在下一个周期中尝试一个节点时,由于条件的原因,它会立即返回到proc。循环遍历$nodes时,每个其他呼叫都会发生同样的情况。似乎只有对其余节点的调用才发生!

要解决您的问题,请将seendfs的每次调用范围隔离开来,我们仍然可以在函数式编程中执行此操作:

dfs = lambda do |node|
  seen = []
  sub_dfs = lambda do |sub_node|
    return if seen.include? sub_node
    seen << sub_node
    $edges[sub_node].each &sub_dfs
  end
  sub_dfs.call node
end

$edges[some_node].each &dfs

现在seen在每次调用dfs时都被安全隔离。

答案 1 :(得分:2)

制作递归lambda的另一种方法:

fac = lambda{|n, &context| n.zero? ? 1 : n * eval("fac.call(#{n-1}) {}",context.binding)}

但是必须用空块调用

fac.call(2){} = 2
fac.call(3){} = 6
fac.call(4){} = 24

binding用于评估lambda范围之外的代码