我有以下代码正确遍历图中的所有节点,如下所示:
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 :(得分:3)
令人惊讶的是,你的问题在递归中不!这实际上是因为seen
中所有来电中的共享$nodes[node].each &dfs
集合。
让我们进行操作:对$nodes[node].first
的调用应该没有任何问题,因为我们知道该代码段适用于任何一个节点。但是有一个问题:seen
没有重置,你已经去了下一个节点!您已经看到了所有节点,因此当您在下一个周期中尝试一个节点时,由于条件的原因,它会立即返回到proc。循环遍历$nodes
时,每个其他呼叫都会发生同样的情况。似乎只有对其余节点的调用才发生!
要解决您的问题,请将seen
与dfs
的每次调用范围隔离开来,我们仍然可以在函数式编程中执行此操作:
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范围之外的代码