2015-06-11 66 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