如何实现if()的惰性求值


10

我目前正在根据以下条件实现表达式评估器(单行表达式,如公式):

  • 输入的表达式被标记化以分隔文字布尔值,整数,小数,字符串,函数,标识符(变量)
  • 我实现了Shunting-yard算法(经过微调以处理具有可变数量的参数的函数)以消除括号并以后缀顺序将运算符以适当的优先级排序
  • 我的调车场只是产生了一个(模拟的)令牌队列(通过数组,我的Powerbuilder Classic语言可以定义对象,但只有动态数组作为本机存储-不是真实列表,没有字典),我使用简单堆垛机

我的评估人员工作得很好,但我仍然缺少if(),我想知道如何进行。

使用分流后缀和基于堆栈的评估,如果我添加if()具有真假部分的另一个函数,则单个if(true, msgbox("ok"), msgbox("not ok"))消息将显示两条消息,而我只希望显示一条消息。这是因为当我需要评估一个函数时,它的所有参数都已经被评估并放置在堆栈中。

您能给我一些if()偷懒实现的方法吗?

我虽然将它们作为一种宏进行处理,但是在早期我还没有条件评估。也许我需要使用队列以外的其他结构来分别保存条件和true / false表达式?目前,表达式在评估之前已被解析,但我还计划将中间表示形式存储为一种预编译的表达式,以供将来评估之用。

编辑:经过一些问题后,我认为我可以构建表达式的树表示形式(AST而不是线性令牌流),从中我可以轻松地忽略自己的一个或另一个分支if()

Answers:


9

这里有两个选择。

1)不要实现if为功能。使它成为具有特殊语义的语言功能。易于执行,但是如果您希望所有功能都可以使用,那么“纯”就少了。

2)实现“按名称调用”的语义,该语义要复杂得多,但允许编译器魔术师在保持if函数而不是语言元素的同时处理惰性求值问题。它是这样的:

if是一个带有两个参数的函数,两个参数都被声明为“按名称”。当编译器看到它正在将某项传递给by-name参数时,它将更改要生成的代码。它不计算表达式并传递值,而是创建一个对表达式求值的闭包,并将其传递。当在函数内部调用一个by-name参数时,编译器会生成代码以评估闭包。


我不确定,但是“关闭”应该是“ thunk”吗?嗯,也许不是;只是看了维基百科页面:“一团糟是无参数的闭包”。

当您说“按名称呼叫”时,您是在全球范围内指代吗?除了全局按名称调用之外,还可以只是实现一个闭包类型,然后if函数仅接受3个闭包,并求值两个(条件,然后取“否则”),但并非所有事物都需要识别为闭包,例如完整的按调用。名称语义
Jimmy Hoffa 2013年

@Matt:术语“ thunk”在编程的上下文中可能意味着其他几件事,而“无参数闭包”并不是我听到的第一个想到的东西。“关闭”要明确得多。
梅森惠勒2013年

1
@JimmyHoffa:当我说“按名称调用”时,我指的是设置函数参数的特定样式,该参数应该是可选的。就像许多现有语言一样,您可以选择按值或按引用传递参数,在这种情况下,您需要选择按名称传递。
梅森惠勒2013年

虽然您对“按名称调用”语义的建议向我展示了一些有趣的观点,但是对于我的评估者(不是一个完整的编译器)来说,这有点过头了,因为我的函数调用是单行的(想想MS-excel公式)。我想我可以通过对堆栈进行伪评估来推断出调用树,从而在令牌排队之后增加一个步骤。从树上更容易知道要丢弃的树枝。

3

而不是具有签名的功能:

object if(bool, object, object)

给它签名:

object if(bool, object function(), object function())

然后,您的if函数将根据条件调用相应的函数,仅评估其中之一。


1

如果您懒惰地编译所有内容,这很容易。您必须具有某种方法来查看某个值是否已被评估,或者是否需要更多评估。

然后,您可以执行以下操作:如果它是文字或变量(是否有那些?或函数名?),则将其压入堆栈。如果它是函数的应用程序,则将其单独编译,然后将入口点压入堆栈。

因此,程序的执行只是循环执行,直到评估堆栈的顶部为止,而不是函数为止。如果未评估它或函数,则调用堆栈顶部指向的代码。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.