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