2011-03-02 41 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用於拉姆達範圍