12

为什么 Scala 引入惰性参数。不应该由JVM管理(对用户不可见)如何初始化值?将控制权交给开发人员并将值定义为惰性值的实际用例是什么?

4

4 回答 4

12

别名参数:主要动机之一是支持 dsls。它们允许您在 API 中拥有非常好的语法,几乎感觉就像它们内置在语言中一样。例如,您可以非常轻松地定义自己的自定义repeat循环:

def repeat(body: =>Unit)(until: =>Boolean): Unit = {
  body
  if (until) {} else repeat(body)(until)
}

然后把它当作语言的一部分来使用。

var i = 0
repeat {
  println(i)
  i += 1
} (i < 3)

或者你可以类似地产生一个像这样的新线程:spawn { println("on the new thread!") },或者你可以像这样对你FileInputStream的 s 进行自动资源管理:withFile("/home/john/.bashrc") { println(_.contents) }

lazy价值观——这里的动机是:

  1. Stream像s这样的惰性数据结构在函数式语言中很流行,您可以使用它们来实现高效的数据结构 a-la Okasaki 的函数式队列。
  2. 避免分配或初始化一些昂贵的资源,如果它们从未在某些对象中使​​用过,例如文件句柄或数据库连接。
  3. 对于由许多 mixin 组成的对象,以正确的顺序初始化对象字段。
  4. 当有许多线程共享一个值时,实现正确的“仅初始化一次”语义(请参阅此处的介绍)。
  5. 为嵌套的单例对象提供翻译方案:

class A { object B }

变成这样:

class A {
  class A$B$
  lazy val B = new A$B$
}
于 2013-11-13T19:21:36.420 回答
9

一种常见的情况是当类的编写者不知道是否将使用昂贵的初始化val。在这种情况下,将val按需初始化。

另一种情况是有机地控制初始化的顺序。通常在初始化某个特定对象之前很久就创建了一个对象val,因为其他类还没有被初始化。在这种情况下,惰性为这种排序自然发生提供了一种方便的方式,而作者无需提出对复杂的多阶段初始化进行排序的总体规划。

于 2013-11-13T14:24:50.743 回答
5

TLDR:因为它吓坏了用户并且由于性能原因

今天的大多数语言都是渴望的。其中一些不是,他们称之为懒惰. 虽然许多编程问题可以通过惰性求值以优美简洁的方式表达,但我认为绝对惰性并不是一个好主意。从主观的角度来看,程序员习惯于以急切的方式思考(尤其是那些来自命令式领域的人),所以用 Haskell 等天真地编写程序可能会让你很困惑。每道菜都只有叉子,不如在叉子和勺子之间选择好,虽然 scala 支持语言级别的惰性评估,但它默认为渴望模型。原因(除了 Martin 和其他语言设计师的个人选择)是 Java 和 Scala 之间的互操作——用一种语言组合这两个世界将是一场噩梦。此外,在 Scala 设计时,JVM 还没有支持这些功能,或多或少只有在 Java 7 中引入了方法句柄,才使高性能惰性 val成为可能(仅在两年前,而 scala 已经存在了十年)。

于 2013-11-13T15:09:17.713 回答
4

我会回答我自己的问题。因此,惰性值非常有用的一个用例是,如果您想创建一个带有循环的不可变数据结构。没有懒惰是不容易的,因为否则你将不得不修改一个已经创建的对象。如果您希望您的对象是不可变的,这是不可能的。让我以简单的循环实现为例。

在此处输入图像描述

因此,在 Scala 中,您可以通过以下方式实现这一点

class Node(inNode: => Node) { lazy val in = inNode }

lazy val node :Node = new Node(new Node(node))

这样你就创建了一个不可变的循环。您可以通过比较参考来验证结果。

scala> node.in
res3: Node = Node@2d928643

scala> node.in.in
res4: Node = Node@3a5ed7a6

scala> node
res5: Node = Node@3a5ed7a6
于 2016-09-07T21:03:55.533 回答