Scala 可以通过引用调用吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4790050/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 02:45:24  来源:igfitidea点击:

Can Scala call by reference?

scalacalling-conventionevaluation-strategy

提问by royco

I know that Scala supports call-by-name from ALGOL, and I think I understand what that means, but can Scala do call-by-reference like C#, VB.NET, and C++ can? I know that Java cannot do call-by-reference, but I'm unsure if this limitation is solely due to the language or also the JVM.

我知道 Scala 支持来自 ALGOL 的按名称调用,我想我明白这意味着什么,但是 Scala 可以像 C#、VB.NET 和 C++ 一样执行按引用调用吗?我知道 Java 不能按引用调用,但我不确定这种限制是否完全是由于语言或 JVM。

This would be useful when you want to pass an enormous data structure to a method, but you don't want to make a copy of it. Call-by-reference seems perfect in this case.

当您想将庞大的数据结构传递给方法,但又不想复制它时,这会很有用。在这种情况下,通过引用调用似乎是完美的。

回答by Rex Kerr

Java and Scala both use call by value exclusively, except that the value is either a primitive or a pointer to an object. If your object contains mutable fields, then there is very little substantive difference between this and call by reference.

Java 和 Scala 都专门使用按值调用,除了值是原始值或指向对象的指针。如果您的对象包含可变字段,那么 this 和 call by reference 之间几乎没有实质性区别。

Since you are always passing pointers to objectsnot the objects themselves, you don't have the problem of having to repeatedly copy a giant object.

由于您总是传递指向对象的指针而不是对象本身,因此您不会遇到必须重复复制一个巨大对象的问题。

Incidentally, Scala's call by name is implemented using call by value, with the value being a (pointer to a) function object that returns the result of the expression.

顺便说一下,Scala 的按名称调用是使用按值调用实现的,值是一个(指向 a 的)函数对象,它返回表达式的结果。

回答by Shelby Moore III

For a language where "everything is an object" and the object reference can not be accessed, e.g. Java and Scala, then every function parameter is a reference passed-by-value at some level of abstraction below the language. However, from the perspective of the semantics of language abstraction, there is either a call-by-reference or call-by-value, depending on whether the function is supplied a copy of the referenced object. In this case, the term call-by-sharing encompasses both call-by-reference and call-by-value at the language level of abstraction. Thus it is correct to say that Java is call-by-value at the level of abstraction below the language semantics (i.e. comparing to how it would be hypothetically translated to C or in bytecode for the virtual machine), while also saying that Java and Scala are (except for builtin types) call-by-reference at the semantics of its "everything is an object" abstraction.

对于“一切都是对象”并且无法访问对象引用的语言,例如 Java 和 Scala,那么每个函数参数都是该语言下面某个抽象级别的按值传递的引用。但是,从语言抽象的语义的角度来看,要么是按引用调用,要么是按值调用,这取决于函数是否提供了被引用对象的副本。在这种情况下,术语共享调用包括语言抽象级别的引用调用和值调用。因此,可以正确地说 Java 在语言语义以下的抽象级别上是按值调用的(即与如何假设地将其转换为 C 或虚拟机的字节码进行比较),

In Java and Scala, certain builtin (a/k/a primitive) types get passed-by-value (e.g. int or Int) automatically, and every user defined type is passed-by-reference (i.e. must manually copy them to pass only their value).

在 Java 和 Scala 中,某些内置(a/k/a 原始类型)类型会自动按值传递(例如 int 或 Int),并且每个用户定义的类型都是按引用传递的(即必须手动复制它们以仅传递它们的价值)。

Note I updated Wikipedia's Call-by-sharing sectionto make this more clear.

注意我更新了 Wikipedia 的Call-by-sharing 部分以使其更清楚。

Perhaps Wikipedia is confused about the distinction between pass-by-value and call-by-value? I thought pass-by-value is the more general term, as it applies to assignment expressions, as well as function application. I didn't bother to try to make that correction at Wikipedia, leave it for others to hash out.

也许维基百科对按值传递和按值调用之间的区别感到困惑?我认为按值传递是更通用的术语,因为它适用于赋值表达式以及函数应用程序。我没有费心尝试在维基百科上进行更正,让其他人自行解决。

There is no difference at level of semantics where "everything is an object" between call-by-reference and call-by-value, when the object is immutable. Thus a language which allows declaration of call-by-value versus call-by-reference (such as the Scala-like language I am developing), can be optimized by delaying the copy-by-value until the object is modified.

当对象不可变时,在按引用调用和按值调用之间“一切都是对象”的语义级别没有区别。因此,允许声明按值调用与按引用调用的语言(例如我正在开发的类 Scala 语言)可以通过延迟按值复制直到对象被修改来优化。



The people who voted this down apparently do not understand what "call-by-sharing" is.

投票否决的人显然不明白“共享呼叫”是什么。

Below I will add the write up I did for my Copute language (which targets JVM), where I discuss evaluation strategy.

下面我将添加我为我的 Copute 语言(针对 JVM)所做的文章,在那里我讨论了评估策略。



Even with purity, no Turing complete language (i.e. that allows recursion) is perfectly declarative, because it must choose an evaluation strategy. Evaluation strategy is the relative runtime evaluation order between functions and their arguments. The evaluation strategy of functions can be strict or non-strict, which is the same as eager or lazy respectively, because all expressions are functions. Eager means argument expressions are evaluated before their function is; whereas, lazy means argument expressions are only evaluated (once) at the runtime moment of their first use in the function. The evaluation strategy determines a performance, determinism, debugging, and operational semantics tradeoff. For pure programs, it does not alter the denotational semantics result, because with purity, the imperative side-effects of evaluation order only cause indeterminism in (i.e are categorically bounded to) the memory consumption, execution time, latency, and non-termination domains.

即使具有纯度,也没有图灵完备语言(即允许递归)是完全声明性的,因为它必须选择评估策略。评估策略是函数及其参数之间的相对运行时评估顺序。函数的求值策略可以是严格的也可以是非严格的,分别与eager或lazy相同,因为所有的表达式都是函数。Eager 意味着参数表达式在它们的函数之前被评估;而惰性意味着参数表达式仅在它们第一次在函数中使用的运行时被评估(一次)。评估策略决定了性能、确定性、调试和操作语义的权衡。对于纯程序,它不会改变指称语义结果,因为在纯程序中,

Fundamentally all expressions are (composition of) functions, i.e. constants are pure functions without inputs, unary operators are pure functions with one input, binary operators are pure functions with two inputs, constructors are functions, and even control statements (e.g. if, for, while) can be modeled with functions. The order that we evaluate these functions is not defined by the syntax, e.g. f( g() ) could eagerly evaluate g then f on g's result or it could evaluate f and only lazily evaluate g when its result is needed within f.

从根本上说,所有表达式都是函数(组合),即常量是没有输入的纯函数,一元运算符是具有一个输入的纯函数,二元运算符是具有两个输入的纯函数,构造函数是函数,甚至是控制语句(例如 if、for、 while) 可以用函数建模。我们评估这些函数的顺序不是由语法定义的,例如 f( g() ) 可以在 g 的结果上急切地评估 g 然后 f 或者它可以评估 f 并且只在 f 中需要它的结果时才懒惰地评估 g。

The former (eager) is call-by-value (CBV) and the latter (lazy) is call-by-name (CBN). CBV has a variant call-by-sharing, which is prevalent in modern OOP languages such as Java, Python, Ruby, etc., where impure functions implicitly input some mutable objects by-reference. CBN has a variant call-by-need (also CBN), where function arguments are only evaluated once (which is not the same as memoizing functions). Call-by-need is nearly always used instead of call-by-name, because it is exponentially faster. Typically both variants of CBN only appear with purity, because of the dissonance between the declared function hierarchy and the runtime order-of-evaluation.

前者(eager)是call-by-value(CBV),后者(lazy)是call-by-name(CBN)。CBV 有一个变种 call-by-sharing,这在现代 OOP 语言如 Java、Python、Ruby 等中很普遍,其中不纯函数通过引用隐式输入一些可变对象。CBN 有一个变体 call-by-need(也是 CBN),其中函数参数只计算一次(这与记忆函数不同)。Call-by-need 几乎总是用来代替 call-by-name,因为它的速度是指数级的。通常情况下,CBN 的两种变体都只是纯粹地出现,因为声明的函数层次结构和运行时的计算顺序之间存在不协调。

Languages typically have a default evaluation strategy, and some have a syntax to optionally force a function to be evaluated in the non-default. Languages which are eager by default, usually evaluate the boolean conjunction (a/k/a "and", &&) and disjunction (a/k/a "or", ||) operators lazily, because the second operand isn't needed in half the cases, i.e. true || anything == true and false && anything == false.

语言通常具有默认评估策略,有些语言具有可选地强制在非默认情况下评估函数的语法。默认情况下急切的语言,通常懒惰地评估布尔合取(a/k/a“and”,&&)和析取(a/k/a“or”,||)运算符,因为不需要第二个操作数在一半的情况下,即 true || 任何东西 == 真和假 && 任何东西 == 假。

回答by Theodore Norvell

Here is how to emulate reference parameters in Scala.

这是在 Scala 中模拟引用参数的方法。

def someFunc( var_par_x : Function[Int,Unit] ) {
    var_par_x( 42 ) // set the reference parameter to 42
}

var y = 0 
someFunc( (x => y=x) )
println(y)

Well, okay, not exactly what Pascal or C++ programmers are used to; but then, very little in Scala is. The upside is that this gives the caller more flexibility with what they can do with the value sent to the parameter. E.g.

好吧,这不是 Pascal 或 C++ 程序员习惯的那样;但是,在 Scala 中很少。好处是这使调用者可以更灵活地处理发送给参数的值。例如

someFunc( (x => println(x) ) )