Compiler construction “按名称调用”和“按值调用”下的打印内容是什么?

Compiler construction “按名称调用”和“按值调用”下的打印内容是什么?,compiler-construction,functional-programming,sml,callbyname,call-by-value,Compiler Construction,Functional Programming,Sml,Callbyname,Call By Value,我用玩具语言编写了这行代码。print-函数获取参数列表并打印这些参数 打印(a,(a:=5,a)) 如果使用“按值调用”或“按名称调用”,输出是否会有所不同?如果是,产出将是什么 可以计算出,a被初始化为0,并且“按值调用”参数通常从左到右求值(在大多数语言中),因此表达式相当于以下内容: arg1 := a // copy value of a to arg1 a := 5 // copy 5 to a arg2 := a // copy value of a to arg2 print(a

我用玩具语言编写了这行代码。
print
-函数获取参数列表并打印这些参数

打印(a,(a:=5,a))

如果使用“按值调用”或“按名称调用”,输出是否会有所不同?如果是,产出将是什么


可以计算出,
a
被初始化为
0

,并且“按值调用”参数通常从左到右求值(在大多数语言中),因此表达式相当于以下内容:

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)
arg1 := function() {return a;}
arg2 := function() {a := 5; return a;}
print(arg1, arg2)
print := function(lazy x, lazy y) {
    writeToOutput(x())
    writeToOutput(y())
}
“按名称调用”显然是一种惰性评估,它会产生如下结果:

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)
arg1 := function() {return a;}
arg2 := function() {a := 5; return a;}
print(arg1, arg2)
print := function(lazy x, lazy y) {
    writeToOutput(x())
    writeToOutput(y())
}
因此,在这种情况下,结果将取决于两件事:

  • 在这种语言中,闭包是通过引用还是通过值捕获变量。如果按值捕获,则
    a:=5
    不会影响第一个闭包捕获的
    a
    的值。然而,大多数允许重新分配局部变量的语言都实现了引用捕获(例如JavaScript)
  • print
    函数决定对其参数求值的顺序取决于它的编写方式
如果闭包按值捕获,
打印(…)
将产生
05
,因为赋值
a:=5
只影响
a
的第二个闭包副本

如果闭包通过引用捕获,那么我只能猜测输出可能是什么。但是,
print
函数很可能会执行如下操作:

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)
arg1 := function() {return a;}
arg2 := function() {a := 5; return a;}
print(arg1, arg2)
print := function(lazy x, lazy y) {
    writeToOutput(x())
    writeToOutput(y())
}
在这种情况下,结果将是相同的(
05
),因为首先计算
x()
,然后处理结果,然后计算
y()。在这种情况下,
a
的值只有在使用
x
完成该功能后才会改变


但这只是猜测
print
可以按任何顺序(和任何次数)对其进行评估。

取决于
print
的实施情况,您对此持什么立场?你读过关于评估策略的哪些材料,还有什么还不清楚?我正在为一个项目编写一个编译器,有人告诉我,在“按名称调用”中,它将打印
05
,而在“按值调用”中,它将打印
55
。他的论点是,在“按值调用”下,它将首先计算所有表达式,然后打印结果。这是真的吗?如果真的有,他似乎是颠倒过来了。具有严格求值和参数从左到右求值的按值调用将传递
0
5
。我认为“按名称调用”意味着通过引用传递参数,但实际上它意味着对参数的惰性求值,在函数体中,参数每“使用”一次都会求值一次。术语“通常是左到右(在大多数语言中)),著名的反例是C、C++和OCaml。在这些语言中,函数参数的求值顺序是未指定的(通常实现为从右到左)。@sepp2k:还有scheme和friends