简体   繁体   English

检测递归函数中的无限循环(R)

[英]Detecting an infinite loop in a recursive function (R)

I have a test df: 我有一个测试df:

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

I want to write a function that inputs a row number and "follows" the y value until it finds a row for which column x = column y. 我想编写一个输入行号并“跟随”y值的函数,直到找到列x =列y的行。

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

So, running get_acc_x(1) returns 1, get_acc_x(9) returns 9, get_acc_x(2) returns 1, get_acc_x(5) also returns 1, etc. 因此,运行get_acc_x(1)返回1,get_acc_x(9)返回9,get_acc_x(2)返回1,get_acc_x(5)也返回1,等等。

But, if I were to run this function on the number 8, it would get into an infinite loop, going back and forth between 3 and 4. What is the easiest way to detect an infinite loop in this situation? 但是,如果我在数字8上运行此函数,它将进入无限循环,在3和4之间来回。在这种情况下检测无限循环的最简单方法是什么? I want to keep track of past inputs so I can stop the function if the same input is used more than once, but I don't know how best to go about keeping track of the inputs. 我想跟踪过去的输入,所以如果不止一次使用相同的输入,我可以停止该功能,但我不知道如何最好地跟踪输入。

You can pass in a parameter marking visited rows: 您可以传入一个标记访问行的参数:

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)
  }
} 

When calling, use get_acc_x(rownum, rep(F, nrow(df)) to pass in an all False param. 调用时,使用get_acc_x(rownum, rep(F, nrow(df))传入一个全False参数。

If you don't want to pass along visited nodes explicitly, you could read them off the call stack using sys.frames . 如果您不想显式传递sys.frames节点,可以使用sys.frames从调用堆栈中读取它们。 If you think the recursion will be reasonably shallow, there shouldn't be too much of a hit to performance, and since it doesn't change the signature, you won't have to modify any of the calling code. 如果你认为递归会相当浅,那么性能不应该太高,并且因为它不会改变签名,所以你不必修改任何调用代码。

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'])
  }
} 

Example: 例:

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

You need to pass the previously seen values as an argument. 您需要将先前看到的值作为参数传递。 I've added a wrapper function which handles passing an initial empty vector. 我添加了一个包装函数来处理传递初始空向量。

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!")
 }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM