scala是否通过复制或引用来维护变量的值?Scala在定义闭包时如何保持变量的值?
例如,在Ruby中,闭包实际上会延长它需要的所有变量的生命周期,它不会复制它们,但会保留对它们的引用,并且变量本身将不符合垃圾回收的条件如果语言有垃圾回收),而关闭是“。 [SKORKIN]
scala是否通过复制或引用来维护变量的值?Scala在定义闭包时如何保持变量的值?
例如,在Ruby中,闭包实际上会延长它需要的所有变量的生命周期,它不会复制它们,但会保留对它们的引用,并且变量本身将不符合垃圾回收的条件如果语言有垃圾回收),而关闭是“。 [SKORKIN]
Scala中的闭包也不会复制对象,它们只会保留对该对象的引用。而且,闭包不会得到它自己的词汇范围,而是它使用周围的词汇范围。
class Cell(var x: Int)
var c = new Cell(1)
val f1 =() => c.x /* Create a closure that uses c */
def foo(e: Cell) =() => e.x
/* foo is a closure generator with its own scope */
val f2 = foo(c) /* Create another closure that uses c */
val d = c /* Alias c as d */
c = new Cell(10) /* Let c point to a new object */
d.x = d.x + 1 /* Increase d.x (i.e., the former c.x) */
println(f1()) /* Prints 10 */
println(f2()) /* Prints 2 */
我无法对垃圾回收评论,但我认为JVM的垃圾收集器将不会删除通过封闭引用,只要关闭仍在引用的对象。
jvm没有关闭,它只有对象。 scala编译器生成匿名类,用于实现代码中每个闭包的每次出现的相应Function trait(取决于签名的参数和结果类型)。
例如,如果由于某种l : List[Int]
,你写l.map(i => i + 1)
,它会被转换为
class SomeFreshName extends Function[Int, Int] {
def apply(i: Int) = i + 1
}
l.map(new SomeFreshName())
在这种情况下,不存在真正的封闭,如在I => i + 1的,不存在自由变量,只有参数我和常量。
如果关闭了一些当地的丘壑,或等效的函数的参数,他们将不得不为构造函数的参数于封闭,实现类要传递:
为l.map(i => s + i)
s是一个字符串参数或本地的方法,它会做
class SomeFreshName(s: String) extends Function[Int, String] {
def apply(i: Int) = s + i
}
l.map(new SomeFreshName(s))
根据需要在构造函数中传递许多参数。
注:如果s是类而不是本地方法的字段,然后s + i
会其实this.s + i
和this
将被传递给匿名类。
垃圾收集器没有什么特别的地方(同样,jvm不知道闭包),简单地说,因为闭包对象有一个对s的引用,s将至少与闭包对象一样长。
请注意,使用匿名类的Java语言中发生的情况完全相同。当一个匿名类使用封闭方法的局部变量时,这些局部变量会默默地添加为匿名类的字段,并在构造函数处传递。
在java中,只有当本地是final
,这相当于scala val
,而不是var
。
的确,在这个实现中,一旦闭包被创建,它就拥有自己关闭的变量的副本。如果它修改它们,那些修改将不会反映在该方法中。如果它们在闭包中被修改,这将不会反映在该方法中。
假设你写
var i = 0
l.foreach{a => println(i + ": " + a); i = i + 1}
println("There are " + i + " elements in the list")
实施描述之前,将
class SomeFreshName(var i: Int) extends Int => Unit {
def apply(a: Int) = println(i + ": " + a); i = i + 1
}
var i = 0
l.foreach(new SomeFreshName(i)
println("There are " + i + " elements in the list")
做的是,将有两个可变i
,一个在方法,和一个在SomeFreshName
。只有SomeFreshName中的一个会被修改,并且最后一个println总是报告0个元素。
Scala通过将引用对象的闭包替换为var来解决他的问题。鉴于一类
class Ref[A](var content: A)
代码首先被
val iRef = new Ref[Int](0)
l.foreach{a =>
println(iRef.content + ": " + a);
iRef.content += iRef.content + 1
}
println("There are " + i + " elements in the list")
更换这当然只是为VAR恰好是采取封闭,而不是每一个VAR完成。这样做,var已被val取代,实际变量值已被移入堆中。现在,关闭可以照常进行,并且可以正常工作。
class SomeFreshName(iRef: Ref[Int]) ...
提及'Ref'的+1。这是我希望很少有人提及的技术细节。 – 2012-07-25 22:19:38
好的 - 我确实想念你的观点,对此感到遗憾。 – sourcedelica 2012-07-29 01:51:01