2015-06-11 71 views
3

我有一個測試DF:在一個遞歸函數檢測無限循環(R)

testdf<-data.frame(x = seq(1,10), y= c(1, 1, 4, 3, 2, 6, 7, 4, 9, 10)) 

testdf 

    x y 
1 1 1 
2 2 1 
3 3 4 
4 4 3 
5 5 2 
6 6 6 
7 7 7 
8 8 4 
9 9 9 
10 10 10 

我想要寫,其輸入行號和「跟隨」的函數的y值,直到它找到一個行對於哪個列x =列y。

get_acc_x<-function(rownum){ 
    if(testdf[rownum, 'x'] == testdf[rownum, 'y']){ 
    return(rownum) 
    }else{ 
    get_acc_x(testdf[rownum, 'y']) 
    } 
} 

所以,運行get_acc_x(1)返回1,get_acc_x(9)返回圖9,get_acc_x(2)返回1,get_acc_x(5)也將返回1等

但是,如果我要在數字8上運行此函數,它將進入無限循環,在3和4之間來回切換。在這種情況下檢測無限循環的最簡單方法是什麼?我想跟蹤過去的輸入,所以如果多次使用相同的輸入,我可以停止該功能,但我不知道如何最好地跟蹤輸入。

回答

3

您可以傳遞一個參數標記訪問行:

get_acc_x<-function(rownum, seen){ 
    if (seen[rownum]) { 
    # Whatever you want to do, cycle detected 
    } 
    seen[rownum] <- T 
    if(testdf[rownum, 'x'] == testdf[rownum, 'y']){ 
    return(rownum) 
    }else{ 
    get_acc_x(testdf[rownum, 'y'], seen) 
    } 
} 

致電時,使用get_acc_x(rownum, rep(F, nrow(df))在所有False參數去傳遞。

1

您需要將先前看到的值作爲參數傳遞。我已經添加了一個處理傳遞初始空向量的包裝函數。

x <- c(1,2,3,4,5,6,7,8,9,10) 
y <- c(1,1,4,3,2,6,7,4,9,10) 
df <- data.frame(x,y) 


get_acc_x <- function(rownum,df) get_acc_x_rec(rownum,df,numeric()) 
get_acc_x_rec<-function(rownum,df,prev){ 
    if(df[rownum, 'x'] == df[rownum, 'y']){ 
return(rownum) 
}else{ 
if(is.element(df[rownum, 'y'],prev)) get_acc_x(df[rownum, 'y'],df,c(prev,rownum)) 
else stop("Damnit!") 
} 
} 
2

如果你不想一起參觀節點傳遞明確的,你可以閱讀過使用sys.frames調用堆棧。如果您認爲遞歸會相當淺,不會對性能造成太大影響,並且由於它不會改變簽名,您不必修改任何調用代碼。

get_acc_x2<-function(rownum){ 
    if(testdf[rownum, 'x'] == testdf[rownum, 'y']){ 
    return(rownum) 
    }else{ 
    rownum %in% sapply(head(sys.frames(), -1), `[[`, "rownum") && 
     stop('infinite recursion detected') 
    get_acc_x2(testdf[rownum, 'y']) 
    } 
} 

實施例:

> get_acc_x2(8) 
Error in get_acc_x2(8) : infinite recursion detected