2012-07-25 7 views
13

scalaはcopyまたはreferenceによって変数の値を保持しますか?クロージャが定義されたとき、Scalaはどのように変数の値を保持しますか?

たとえばRubyでは、クロージャは実際に必要なすべての変数の有効期間を延長しますが、コピーはされませんが、参照は保持され、変数自体はガベージコレクションの対象になりません言語にガベージコレクションがある場合)。 [SKORKIN]

答えて

12

また、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のガベージコレクタがいる限り閉鎖がまだ参照されているように、閉鎖によって参照されているオブジェクトを削除しないことを前提としています。

20

jvmにはクロージャがありません。オブジェクトにのみ存在します。スカラコンパイラは、コード内のクロージャの各出現について適切な関数特性(シグネチャの引数と結果のタイプに応じて)を実装する匿名クラスを生成します。例えば

一部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、なしあり自由変数、引数iと定数のみ。 sが文字列パラメータまたはAであるl.map(i => s + i)ため

あなたには、いくつかのローカルヴァルス、または同等の関数のパラメータを超える閉じた場合、彼らは閉鎖-実装クラスにコンストラクタのパラメータとして渡される必要がありますメソッドのローカルでは、必要に応じてコンストラクタに多くのパラメータを渡します。

class SomeFreshName(s: String) extends Function[Int, String] { 
    def apply(i: Int) = s + i 
} 
l.map(new SomeFreshName(s)) 

注:■はなく、メソッドのローカルのクラスのフィールドだった場合は、s + iが実際this.s + iにあるであろう、とthisは匿名クラスに渡されます。

ガベージコレクタには何も特別なものはありません(やはり、jvmはクロージャを認識しません)。クロージャオブジェクトにはsへの参照が含まれているため、少なくともクロージャオブジェクトと同程度に長くなります。

匿名クラスのjava言語ではまったく同じことが起こることに注意してください。匿名クラスが、囲むメソッドのローカルを使用する場合、これらのローカルは匿名クラスのフィールドとして自動的に追加され、コンストラクタで渡されます。

javaの場合、ローカルがfinalvarではなく、scala valに相当)である場合にのみ、これが許可されます。

実際には、この実装では、クロージャが作成されるとすぐに、そのクロージャが閉じた変数のコピーを持ちます。変更すると、その変更はメソッドに反映されません。クロージャで修正された場合、これはメソッドに反映されません。

あなたは

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") 

方法では1、およびSomeFreshNameで1、2変数iがあるだろう、ということやっているだろう前に説明した実装を作成するとします。 SomeFreshName内のものだけが変更され、最後のprintlnは常に0個の要素を報告します。

Scalaは、closureで取り込まれたvarを参照オブジェクトで置き換えることで、問題を解決します。クラス

class Ref[A](var content: A) 

与えられたコードは、最初にこれだけの閉鎖が取るべきたまたまVARに、必ずしもすべてのVARに当然のことながら行われ

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がvalに置き換えられ、実際の変数の値がヒープに移動しました。さて、閉鎖はいつものように行うことができ、それは動作します。 'Ref`に言及すると

class SomeFreshName(iRef: Ref[Int]) ... 
+2

+1です。それは私が言及する人がほとんどいないと思う技術的な詳細です。 –

+0

さて、私はあなたのことを恋しく思っていました。 – sourcedelica

関連する問題