Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
`Scala中的def`vs`val`vs`lazy val`求值_Scala_Properties_Lazy Evaluation - Fatal编程技术网

`Scala中的def`vs`val`vs`lazy val`求值

`Scala中的def`vs`val`vs`lazy val`求值,scala,properties,lazy-evaluation,Scala,Properties,Lazy Evaluation,我的理解正确吗 def在每次访问时都进行评估 lazy val一旦被访问就会被评估 val在进入执行范围后进行评估 是的,但对于第三个语句,我会说“当该语句被执行时”,因为,例如: def foo() { new { val a: Any = sys.error("b is " + b) val b: Any = sys.error("a is " + a) } } 这将给出“b为空”b不会被计算,也不会抛出其错误。但一旦控件进入块,它就在范围

我的理解正确吗

  • def
    在每次访问时都进行评估

  • lazy val
    一旦被访问就会被评估

  • val
    在进入执行范围后进行评估


是的,但对于第三个语句,我会说“当该语句被执行时”,因为,例如:

def foo() {
    new {
        val a: Any = sys.error("b is " + b)
        val b: Any = sys.error("a is " + a)
    }
}

这将给出
“b为空”
b
不会被计算,也不会抛出其错误。但一旦控件进入块,它就在范围内。

您是正确的。从以下方面获取证据:

从“3.3.1方法类型”(对于
def
):

无参数方法命名每次重新计算的表达式 将引用无参数方法名称

从“4.1价值声明和定义”中:

值定义
val x:T=e
x
定义为从
e
的评估

惰性值定义计算其右侧
e
第一个 访问该值的时间


是的,但有一个很好的技巧:如果您有一个惰性值,并且在第一次求值时它将得到一个异常,下次您尝试访问它时,它将尝试重新求值

下面是一个例子:

scala> import io.Source
import io.Source

scala> class Test {
     | lazy val foo = Source.fromFile("./bar.txt").getLines
     | }
defined class Test

scala> val baz = new Test
baz: Test = Test@ea5d87

//right now there is no bar.txt

scala> baz.foo
java.io.FileNotFoundException: ./bar.txt (No such file or directory)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.<init>(FileInputStream.java:137)
...

// now I've created empty file named bar.txt
// class instance is the same

scala> baz.foo
res2: Iterator[String] = empty iterator
scala>导入io.Source
导入io.Source
scala>类测试{
|lazy val foo=Source.fromFile(“./bar.txt”).getLines
| }
定义类测试
scala>val baz=新测试
baz:测试=Test@ea5d87
//现在没有bar.txt
scala>baz.foo
java.io.FileNotFoundException:./bar.txt(无此类文件或目录)
在java.io.FileInputStream.open(本机方法)
位于java.io.FileInputStream。(FileInputStream.java:137)
...
//现在我已经创建了一个名为bar.txt的空文件
//类实例是相同的
scala>baz.foo
res2:迭代器[字符串]=空迭代器

def
定义了一个方法。当您调用该方法时,当然会运行该方法

val
定义一个值(不可变变量)。赋值表达式在值初始化时计算


lazy val
定义了一个初始化延迟的值。它将在第一次使用时初始化,因此随后将对赋值表达式进行求值。

应该指出,在使用运行时才知道的值时,val的使用存在一个潜在的陷阱

例如,
request:HttpServletRequest

如果你说:

val foo = request accepts "foo"
您将在val初始化时得到一个空指针异常,请求没有foo(只有在运行时才知道)


因此,根据访问/计算的费用,def或lazy val是运行时确定的值的适当选择;或者val本身是一个匿名函数,用于检索运行时数据(尽管后者似乎更边缘化)

选择
def
而不是
val
,特别是在抽象类(或用于模拟Java接口的特征)中,一个很好的理由是,您可以在子类中用
val
覆盖
def
,但不能反过来


关于懒惰,有两件事我可以看到,一个人应该牢记在心。第一个是
lazy
引入了一些运行时开销,但我想您需要对特定情况进行基准测试,以确定这是否对运行时性能有重大影响。
lazy
的另一个问题是,它可能会延迟引发异常,这可能使您的程序更难推理,因为该异常不是预先引发的,而是仅在第一次使用时才会引发。

通过在程序中每次出现名称时替换名称及其RHS表达式来计算def限定的名称。因此,此替换将在程序中名称出现的每个位置执行

当控件到达其RHS表达式时,将立即计算由val限定的名称。因此,每次名称出现在表达式中时,它都将被视为此计算的值


由lazy val限定的名称遵循与val限定相同的策略,例外情况是,只有当控件到达第一次使用名称的点时,才会对其RHS进行评估

我想通过我在REPL中执行的示例来解释差异。我相信这个简单的示例更容易理解掌握并解释概念差异

在这里,我创建了一个val result1、一个lazy val result2和一个def result3,每个都有一个类型字符串

A)。val

scala> val result1 = {println("hello val"); "returns val"}
hello val
result1: String = returns val
scala> lazy val result2 = {println("hello lazy val"); "returns lazy val"}
result2: String = <lazy>
这里执行println是因为这里计算了result1的值。因此,现在result1将始终引用其值,即“returns val”

现在,您可以看到result1现在引用了它的值。注意,这里不执行println语句,因为result1的值在第一次执行时已经计算过。所以,从现在开始,result1将始终返回相同的值,println语句将不再执行,因为已经执行了获取result1值的计算

B)。懒惰的val

scala> val result1 = {println("hello val"); "returns val"}
hello val
result1: String = returns val
scala> lazy val result2 = {println("hello lazy val"); "returns lazy val"}
result2: String = <lazy>
现在,当我再次引用result2时,这一次,我们将只看到它持有的值,println语句将不会执行。从现在起,result2的行为将简单地像val一样,并始终返回其缓存的值

scala> result2
res2: String = returns lazy val
C)。def

对于def,每次调用result3时都必须计算结果。这也是我们在scala中将方法定义为def的主要原因,因为每次在程序中调用方法时,方法都必须计算并返回一个值

scala> def result3 = {println("hello def"); "returns def"}
result3: String

scala> result3
hello def
res3: String = returns def

scala> result3
hello def
res4: String = returns def

顺便问一下,除非需要相反的定义,否则定义所有的VAL不是一个好主意吗?@Ivan我怀疑这会减少