26

我知道 Scala 支持 ALGOL 的按名称调用,我想我理解这意味着什么,但是 Scala 可以像 C#、VB.NET 和 C++ 那样做按引用调用吗?我知道 Java 不能进行引用调用,但我不确定这种限制是完全由于语言还是 JVM 造成的。

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

4

4 回答 4

51

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

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

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

于 2011-01-25T04:47:47.620 回答
4

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

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

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

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

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


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

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


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

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

前者(eager)是按值调用(CBV),后者(惰性)是按名称调用(CBN)。CBV 有一个变种 call-by-sharing,这在现代 OOP 语言如 Java、Python、Ruby 等中很普遍,其中不纯函数通过引用隐式输入一些可变对象。CBN 有一个变体 call-by-need(也是 CBN),其中函数参数只评估一次(这与记忆函数不同)。几乎总是使用按需调用而不是按名称调用,因为它的速度呈指数级增长。通常,由于声明的函数层次结构和运行时评估顺序之间的不协调,CBN 的两种变体都只能以纯度出现。

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

于 2011-02-16T03:48:44.423 回答
0

这是在 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)

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

someFunc( (x => println(x) ) )
于 2015-03-28T20:56:52.087 回答
0

我正在阅读有关此主题的内容,并且看到了此问题和答案。如果您将其复制并粘贴到文本文件中并将其另存为 *.bat (如 testref.bat),我就会使用这个示例:)


::#! 
@echo off 
call scala -savecompiled %0 %* 
goto :eof 
::!# 

  class X( var x: Array[String] )
  val mybb = new X(Array("ss", "dd"))
  println("Printing x array:")
  mybb.x.foreach( e => println(e))

  mybb.x = mybb.x :+ "Carlos"

  println("Printing x array, carlos added:")
  mybb.x.foreach( e => println(e))

  updateMyVar(mybb)

  println("Printing x array, kassab added:")
  mybb.x.foreach( e => println(e))


  def updateMyVar( mycc: X ): Unit =
  {
    mycc.x = mycc.x :+ "Kassab"
  }

我只是想确认它是否真的是一种通过引用调用变量的方法?

于 2020-07-31T22:09:43.403 回答