2011-03-02 43 views
1

我有下面的代码是正确遍历所有节点的图形像这样:问题与Ruby的递归拉姆达呼叫

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]列表的第一个元素。是什么赋予了?

+0

确定吗?它似乎工作正常:http://ideone.com/RwUed – 2011-03-02 19:37:12

回答

3

令人惊讶的外评估代码够了,你的问题是不是在递归!实际上是因为$nodes[node].each &dfs中所有呼叫共享seen

让我们来看看这个操作:$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的安全隔离。

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用于拉姆达范围