我用玩具语言编写这行代码。 print
- 函数接受一个参数列表并打印出来。
print(a, (a := 5, a))
如果我使用call-by-value或call-by-name,输出会有区别吗?如果是这样,产出会是什么。
可以假设a
已初始化为0
。
答案 0 :(得分:4)
使用“按值调用”参数通常从左到右(在大多数语言中)进行评估,因此表达式将等同于:
arg1 := a // copy value of a to arg1
a := 5 // copy 5 to a
arg2 := a // copy value of a to arg2
print(arg1, arg2) // print(0, 5)
“call-by-name”显然是一种懒惰的评价形式,会产生这样的结果:
arg1 := function() {return a;}
arg2 := function() {a := 5; return a;}
print(arg1, arg2)
所以在这种情况下,结果将取决于两件事:
a := 5
将不会影响第一个闭包捕获的a
的值。但是,大多数允许重新分配局部变量的语言都实现了按引用捕获(例如JavaScript)。print
函数决定评估其参数的顺序取决于它的编写方式。如果按值捕获闭包,print(…)
将产生0 5
,因为作业a := 5
仅影响第二个闭包的a
副本。
如果通过引用捕获闭包,那么我只能猜测输出可能是什么。但print
函数很可能会做这样的事情:
print := function(lazy x, lazy y) {
writeToOutput(x())
writeToOutput(y())
}
在这种情况下,结果将是相同的(0 5
),因为首先评估x()
,处理结果,然后评估y()
。在这种情况下,a
的值直到函数完成x
后才会更改。
但这只是猜测; print
可以按任何顺序(以及任意次数)评估它们。