【问题标题】:Defining variables in scala using def使用 def 在 scala 中定义变量
【发布时间】:2016-11-28 13:55:48
【问题描述】:

scala中def用于定义方法,val、var用于定义变量。

考虑以下代码:

scala> def i = 3
i: Int

scala> i.getClass()
res0: Class[Int] = int

scala> val v = 2
v: Int = 2

scala> v.getClass()
res1: Class[Int] = int

scala> println(v)
2

scala> println(i)
3

scala> i+v
res4: Int = 5

scala> def o = () => 2+3
o: () => Int

scala> o.getClass()
res5: Class[_ <: () => Int] = class $$Lambda$1139/1753607449

为什么变量定义可以使用def?如果它定义了一个返回 Int 的函数,那么为什么 getClass 显示 Int 而不是函数对象?

【问题讨论】:

    标签: scala


    【解决方案1】:

    valvar 声明不同,def i = 3 不是变量声明。您正在定义一个方法/函数,它返回一个常量 3i 不带任何参数。

    使用 valvar 的声明会立即被评估,但如果是惰性 val 和 def 评估,则会在显式调用时进行。

    i 是一个非参数函数。为了消除混淆,您也可以使用空括号声明它

    def i() = 3
    

    lazy valdef 的区别是

    1. lazy val 被延迟评估,结果被缓存。这意味着进一步

    2. 每次调用方法名时都会评估def声明。

    使用 Scala REPL 的示例

    scala> lazy val a = { println("a evaluated"); 1}
    a: Int = <lazy>
    
    scala> def i = { println("i function evaluated"); 2}
    i: Int
    
    scala> a
    a evaluated
    res0: Int = 1
    
    scala> a
    res1: Int = 1
    
    scala> a
    res2: Int = 1
    
    scala> i
    i function evaluated
    res3: Int = 2
    
    scala> i
    i function evaluated
    res4: Int = 2
    
    scala> i
    i function evaluated
    res5: Int = 2
    

    注意a 只被评估一次,a 的进一步调用返回缓存的结果,即惰性 val 在被调用时被评估一次,结果被永久存储。所以你会看到一次 println 输出

    Notice 函数在每次调用时都会被评估。在这种情况下,每次调用函数时都会看到 println 输出

    一般约定

    当方法有副作用时使用空参数列表并在纯时不使用它们。

    编辑

    scala> def i = 1
    i: Int
    
    scala> :type i
    Int
    
    scala> :type i _
    () => Int
    

    【讨论】:

    • 感谢您的详细解释。但是如果def 声明了一个函数或者更确切地说是创建一个函数对象,那么为什么getClass 返回类型Int?我希望看到某种函数对象。例如:Java8中的lambda实现了java.util.function.*包下的接口之一。
    • @KshitizSharma Scala 具有统一访问原则,因此客户端不需要知道他们是在访问字段还是调用无副作用的方法。
    • 是的,如果您担心结果值。但是在通过反射分析一个函数时,它应该说它是一个函数而不是一个 Int。
    • @KshitizSharma 当您说 i.getClass 时,您正在调用 i 并在返回类型为 Int 时执行 getClass
    • scala&gt; def i = 1 i: Int scala&gt; :type i Int scala&gt; :type i _ () =&gt; Int @KshitizSharma
    【解决方案2】:

    编辑:我的回答解决了修订 #3 的问题。

    在编译过程中查看代码非常有用,您可以在其中查看代码实际翻译成的内容。以下简单程序:

    object TestApp {
        def definedVal = 3
        val valVal = 3
        lazy val lazyValVal = 3
    
        def main(args: Array[String]) {
            println(definedVal)
            println(valVal)
            println(lazyValVal)
        }
    } 
    

    被翻译成以下内容(使用-Xprint:mixin 编译器选项):

    [[syntax trees at end of                     mixin]] // test.scala
    package <empty> {
      object TestApp extends Object {
        @volatile private[this] var bitmap$0: Boolean = false;
        private def lazyValVal$lzycompute(): Int = {
          {
            TestApp.this.synchronized({
              if (TestApp.this.bitmap$0.unary_!())
                {
                  TestApp.this.lazyValVal = 3;
                  TestApp.this.bitmap$0 = true;
                  ()
                };
              scala.runtime.BoxedUnit.UNIT
            });
            ()
          };
          TestApp.this.lazyValVal
        };
    
        def definedVal(): Int = 3;
    
        private[this] val valVal: Int = _;
        <stable> <accessor> def valVal(): Int = TestApp.this.valVal;
    
        lazy private[this] var lazyValVal: Int = _;
        <stable> <accessor> lazy def lazyValVal(): Int = if (TestApp.this.bitmap$0.unary_!())
          TestApp.this.lazyValVal$lzycompute()
        else
          TestApp.this.lazyValVal;
    
        def main(args: Array[String]): Unit = {
          scala.this.Predef.println(scala.Int.box(TestApp.this.definedVal()));
          scala.this.Predef.println(scala.Int.box(TestApp.this.valVal()));
          scala.this.Predef.println(scala.Int.box(TestApp.this.lazyValVal()))
        };
        def <init>(): TestApp.type = {
          TestApp.super.<init>();
          TestApp.this.valVal = 3;
          ()
        }
      }
    }
    

    从上面的输出可以得出以下结论:

    1. definedVal其实是一个方法。
    2. valVal 是在构造函数中初始化的字段,具有自动生成的访问器。
    3. 对于惰性字段lazyValVal,编译器会生成compute 方法,该方法仅在第一次访问该字段时调用一次。

    【讨论】:

      【解决方案3】:

      几乎没有不同的概念。按名称调用,按价值调用,按需要调用。所有 def 本质上都是按名称调用。使用 def 定义变量是什么意思? 对我来说看起来像重复: Call by name vs call by value in Scala, clarification needed 更多详情见 wiki:https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_name

      【讨论】:

      • Call-by-name 是一种参数评估语义。执行 OP 对 def 所做的事情在某些情况下恰好表现相同,但不应与它混淆。此外,这不是懒惰的评估。那是另一回事。
      • 哦..我明白了,我混合了“按名称呼叫”和“按需要呼叫”。 en.wikipedia.org/wiki/Evaluation_strategy#Call_by_name 。但是,这是一个重复的问题:)
      猜你喜欢
      • 2013-11-05
      • 1970-01-01
      • 1970-01-01
      • 2015-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-14
      • 2011-05-25
      相关资源
      最近更新 更多