我目前正在为非常有限的面向对象语言开发编译器。我希望将所有值视为对象,并将这些值上的运算符实现为方法。编译器将程序转换为基于堆栈的虚拟机的汇编程序。
在编译期间,我将整数文字转换为特殊“整数”类的对象。算术运算符作为该类的方法实现(使用内联汇编程序)。因此4 + 5
基本上等于4.add(5)
。
我现在面临的问题是布尔值的特殊情况。如果有if
声明:
if(10 > 5 || 12 < 10)
目前这将转换为:10.greaterThan(5).or(12.lessThan(10))
现在很明显,这些整数文字也可以调用具有副作用的函数。在这种情况下,将这些二元运算符实现为方法调用会产生问题,因为短路评估变得不可能。
所以我的问题是:
其他语言如何实现短路评估,但仍将每个值视为对象?
根据维基百科“ALGOL 68使用”程序“实现用户定义的短路操作员和程序”。 - 这是如何工作的?
答案 0 :(得分:4)
我认为,通常的技术涉及call by name或call by need。我们的想法是or
的参数不是比较的结果,而是将比较本身转换为thunk,每当(通过名称调用)转换为结果值或者第一次(需要时)需要它。
将表达式转换为thunk时,基本上是创建一个匿名函数,并且可以像这样处理编译问题。它涉及将表达式编译为将评估表达式的代码。它还涉及创建一个对象(thunk本身),该对象引用(或包含)表达式使用的局部变量的副本,以及指向表达式的已编译代码的指针。该对象需要与您的布尔类接口兼容,以便使用它的代码不必关心它是否具有真正的布尔值或thunk如此。每当有人需要布尔值时,thunk将执行编译的表达式代码并提供结果值。
如果按姓名打电话对你来说已经足够了,那就是它的全部内容。对于按需调用,缓存评估结果会增加复杂性。
答案 1 :(得分:1)
IIRC ALGOL使用按名称调用的参数,这就是该解决方案有效的原因。
||
运算符可以实现为(伪代码):
if (cond1) goto label;
if (cond2) goto label;
label:
<body>
nomatch:
...
对于&&
,可以完成上述反转。
答案 2 :(得分:1)
重新:根据维基百科“ALGOL68使用”程序“实现用户 定义的短路运营商&amp;程序。“ - 这是如何工作的?
Algol68-r0(原始/未经修订的定义)有两个与短路评估相关的概念。
想象一下编码器想要定义一个执行“短路评估”的矩阵乘法运算符,所以一旦左参数为“零”矩阵,那么就会对右侧进行进一步的评估......这样用户定义的定义可以是:
MODE MAT = FLEX[0,0]REAL;
OP ISZERO = (MAT a)BOOL: ¢ insert actual code here ¢ ~;
PRIO TIMESF = 7;
OP TIMESF = (MAT a, PROC MAT in b)MAT:
IF ISZERO a THEN a ELSE MAT b = in b; ¢ insert actual code here ¢ ~ FI;
MAT a = 0, b = 16, c = 25;
print(a TIMESF b TIMESF c) ¢ would print 0 without calculating 16*25 ¢
相反,编码人员希望并行评估左手和右手参数。这样的用户定义定义可以是:
PRIO TIMESPAR = 7;
OP TIMESPAR = (MAT a, MAT b)MAT: ¢ insert actual code here ¢ ~;
逗号告诉编译器可以任意顺序,甚至并行地评估左手和右手。 (这使编译器可以选择优化评估)
或者编码员可能想强制评估是连续的:
PRIO TIMESSEQ = 7;
OP TIMESSEQ = (MAT a; MAT b)MAT: ¢ insert actual code here ¢ ~;
在这种情况下,“;”被称为“gomma”,用于“逗留逗号”。
Algol68-r1(1974年修订,在sourceforge上为windows和linux提供)删除了所有这些功能,让编码人员手动/专门应用“程序”......例如
第一组矩阵定义是相同的:
MODE MAT = FLEX[0,0]REAL;
PRIO TIMESF = 7;
OP TIMESF = (MAT a, PROC MAT in b)MAT:
IF ISZERO a THEN a ELSE MAT b = in b; ¢ insert actual code here ¢ ~ FI;
MAT a = 0, b = 16, c = 25; ¢ 3 1x1 matrices are "widening" from REAL numbers ¢
但用法不同,注意使用两个lambdas(MAT:b TIMES MAT:c)和(MAT:c):
print(a TIMESF MAT:b TIMESF MAT:c) ¢ would print 0 without calculating 16*25 ¢
Algol68-r1中保留了透明的去毛刺和扩宽。