【问题标题】:Can Scala call by reference?Scala可以通过引用调用吗?
【发布时间】:2011-06-14 23:21:03
【问题描述】:

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

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

【问题讨论】:

  • 我不认为你可以在 Scala 中按值传递数据结构。
  • @Gabe 它仍然是 pass/call-by-value(AnyRef 类型的传递值的底层对象引用和 Python/Java 人喜欢滥用“传递引用“这个术语;-)。但是,按引用传递/调用意味着在函数中设置变量将设置传入变量的值(例如C++中的&ref或VB中的ByRefout/ref在 C# 中)。这可以通过传递的对象和状态突变模拟,但它不一样(C 可以通过修改指针引用的值来模拟它)。
  • @pst:我指的是“将巨大的数据结构传递给方法”部分。要么 Scala 有一种我不知道在将数据结构传递给函数时复制数据结构的方式,要么数据结构已经通过引用传递,并且来自 OP 的引用无关紧要。

标签: scala calling-convention evaluation-strategy


【解决方案1】:

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

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

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

【讨论】:

  • 你能详细说明你关于可变字段有影响的观点吗?
  • 好吧,假设你想改变一个变量x。您可以在 C 中将其传递为 int *x。在 Scala 中,您将有一个类保存该变量:class X(var x: Int)。现在,如果您将该类传递给某个方法,它可以更改 x 的值。
  • 说 Scala 只使用按值调用是混淆了语言和它的实现。如果我们使用汇编程序,那么所有语言都专门按值调用。 Scala可以在 JVM 上支持按引用调用,就像创建一个容器对象来传递引用或原语,然后在调用返回时从该容器中读取。
  • 请不要将名称调用与值调用与引用传递与值传递混淆。前者只对不纯的语言有影响。对于纯语言传递引用没有意义 - 它只是一个无用的额外间接。后者对纯语言和非纯语言的语义都有影响。纯语言调用是按名称调用或按值调用,选择将对终止属性产生影响。
  • 该语言对于按名称调用或按值调用参数切换具有某种语法糖。 Martin Odersky 在他的 Scala Coursera 函数式编程课程中详细解释了这一点。它也显示在另一个 Stackoverflow 问题中。该符号是一个前导的“=>”,用于改变传递参数的行为,请参阅:stackoverflow.com/questions/13337338/… 您可能会争辩说这与引用调用并不完全相同,但至少在委派执行时它具有相似的效果.
【解决方案2】:

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

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

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

也许 Wikipedia 对 pass-by-value 和 call-by-value 之间的区别感到困惑?我认为按值传递是更通用的术语,因为它适用于赋值表达式以及函数应用程序。我没有费心去尝试在 Wikipedia 上进行更正,留给其他人讨论吧。

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


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

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


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

从根本上说,所有表达式都是函数的(组合),即常量是没有输入的纯函数,一元运算符是具有一个输入的纯函数,二元运算符是具有两个输入的纯函数,构造函数是函数,甚至是控制语句(例如,如果, 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”,||)运算符,因为不需要第二个操作数在一半的情况下,即正确||任何东西 == 真假 && 任何东西 == 假。

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      我正在阅读有关此主题的内容,并且看到了此问题和答案。 如果您将其复制并粘贴到文本文件中并将其另存为 *.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"
        }
      
      

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-04-09
        • 2016-06-13
        • 2018-06-06
        • 2011-02-05
        • 1970-01-01
        • 1970-01-01
        • 2010-11-11
        相关资源
        最近更新 更多