Recursion 如何在自定义表达式计算器中检测循环逻辑或递归?

Recursion 如何在自定义表达式计算器中检测循环逻辑或递归?,recursion,evaluation,Recursion,Evaluation,我已经编写了一个实验函数计算器,它允许我将简单函数绑定在一起,这样当变量发生变化时,依赖这些变量的所有函数(以及依赖这些函数的函数等)都会同时更新。我这样做的方式不是在输入函数时立即计算它,而是存储函数。只有当请求输出值时,我才对函数进行求值,每次请求输出值时,我都会对函数进行求值 例如: pi = 3.14159 rad = 5 area = pi * rad * rad perim = 2 * pi * rad a = b b = c c = a 我将'pi'和'rad'定义为变量(返回

我已经编写了一个实验函数计算器,它允许我将简单函数绑定在一起,这样当变量发生变化时,依赖这些变量的所有函数(以及依赖这些函数的函数等)都会同时更新。我这样做的方式不是在输入函数时立即计算它,而是存储函数。只有当请求输出值时,我才对函数进行求值,每次请求输出值时,我都会对函数进行求值

例如:

pi = 3.14159
rad = 5
area = pi * rad * rad
perim = 2 * pi * rad
a = b
b = c
c = a
我将'pi'和'rad'定义为变量(返回常数的函数),将'area'和'perim'定义为函数。无论何时‘pi’或‘rad’发生变化,我都希望‘area’和‘perim’的结果会发生同样的变化。同样,如果有任何函数依赖于“区域”或“perim”,那么这些函数的结果也会发生变化

这一切都按预期进行。这里的问题是当用户引入递归时——不管是偶然的还是有意的。我的语法中没有逻辑——它只是一个求值器——因此我无法为用户提供一种“突破”递归的方法。我想阻止它发生,这意味着我需要一种方法来检测它,并将有问题的输入声明为无效

例如:

pi = 3.14159
rad = 5
area = pi * rad * rad
perim = 2 * pi * rad
a = b
b = c
c = a
现在计算最后一行会导致StackOverflowException(前两行计算为“0”-未声明的变量/函数等于0)。我想做的是检测循环逻辑情况,并禁止用户输入这样的语句。不管循环逻辑隐藏得有多深,我都想这样做,但我不知道如何去做

顺便说一句,在幕后,输入字符串通过一个简单的扫描器转换为令牌,然后通过一个手写的递归下降解析器转换为抽象语法树,然后计算AST。语言是C#,但我不是在寻找代码解决方案-仅逻辑就可以了

注意:这是一个我用来学习解析器和编译器如何工作的个人项目,所以它不是任务关键型的-但是我从中获得的知识我确实计划在某个时候投入到实际生活中。如果你们能提供任何帮助,我们将不胜感激。=)


编辑:如果有人好奇,请描述我为什么要学习这个,以及我从中得到了什么。

我过去也遇到过类似的问题。 我的解决方案是在递归表达式以检查语法时将变量名推送到堆栈上,并在退出递归级别时弹出它们

在我将每个变量名推到堆栈上之前,我会检查它是否已经存在。 如果是,那么这是一个循环引用。 我甚至能够在循环引用链中显示变量的名称(因为它们将在堆栈上,并且可以按顺序弹出,直到我找到不合适的名称)

编辑:当然,这是针对单一公式。。。对于您的问题,变量分配的循环图是更好的方法。

解决方案(可能不是最好的)是创建依赖关系图

每次添加或更改函数时,都会检查依赖关系图中是否存在循环

这是可以缩短的。每次添加或更改函数时,都要对其进行标记。如果评估结果导致调用被标记的函数,则有一个循环

例如:

a=b

  • 旗a
  • 评估b(未找到)
  • 解渣
b=c

  • b旗
  • 评估c(未找到)
  • 解渣b
c=a

  • c旗
  • 评估a
  • 评估b
  • 评估c(标记)->循环,放弃对c的更改
  • 解渣c

在回答关于答案二的评论时:

(很抱歉,我的openid创建工作搞砸了,所以我以后必须把旧的东西链接起来……)

如果将“flag”切换为“push”,将“unflag”切换为“pop”,则情况基本相同:) 使用堆栈的唯一优点是,无论深度如何,都可以轻松地提供有关循环的详细信息。(对错误消息有用:))


安德鲁

我会试试看——听起来这是一个很好的处理方法。我会告诉你事情的进展。非常感谢好吧,希望能成功。检查我的澄清(和下面的答案),如果它比它应该的更笨拙:)实际上一点也不笨拙-这完全符合规定,似乎可以处理任何级别的循环逻辑。感谢a ton=)不过,作为将来的参考-如何构建变量赋值的循环图?我认为这也会很好地工作,并且比基于堆栈的方法需要更少的内存。非常感谢对-这就是我接受这个答案的原因。=)GL让OpenID玩得很好。