2012-07-02 10 views
7

Scalaは、怠惰なvarsを作成することを許可していません。それは意味をなさない。scalaで怠惰なvarを作る

しかし、私は同様の機能を使いたいユースケースにぶつかってきました。私は怠惰な変数ホルダーが必要です。時間のかかるアルゴリズムによって計算されるべき値が割り当てられてもよい。しかし、後に別の値に再割り当てされる可能性があり、最初の値の計算をまったく呼び出さないことをお勧めします。コードのこの作品のみCALC2()を呼び出す必要があり

lazy var value : Int = _ 
val calc1 :() => Int = ... // some calculation 
val calc2 :() => Int = ... // other calculation 
value = calc1 
value = calc2 
val result : Int = value + 1 

いくつかの魔法のVARの定義があると仮定し

例、私は暗黙の変換として、このコンテナを書くことができますどのようにアイデアを持っている

をCALC1ありません特別なコンテナクラス。私はこの作品

答えて

1
var value:() => Int = _ 
lazy val calc1 = {println("some calculation"); 1} 
lazy val calc2 = {println("other calculation"); 2} 
value =() => calc1 
value =() => calc2 

scala> val result : Int = value() + 1 
other calculation 
result: Int = 3 
6

不要なコードを書く必要はありません任意の埋め込まれたScalaの機能があれば、私は骨董品だ:それは、広く適用可能であるので、少し私を暗黙の悩みを持つ

var value:() => Int = _ 
val calc1:() => Int =() => { println("calc1"); 47 } 
val calc2:() => Int =() => { println("calc2"); 11 } 
value = calc1 
value = calc2 
var result = value + 1 /* prints "calc2" */ 

implicit def invokeUnitToInt(f:() => Int): Int = f() 

がいます予期しないアプリケーションやあいまいな暗黙のコンパイラ・エラーが発生する可能性があります。あなたは、単にコンパイラが自分の作品行うと、このようSTHを行うことができ

lazy val calc3 = { println("calc3"); 3 } 
lazy val calc4 = { println("calc4"); 4 } 

class LazyVar[A] { 
    private var f:() => A = _ 
    def value: A = f() /* getter */ 
    def value_=(f: => A) = this.f =() => f /* setter */ 
} 

var lv = new LazyVar[Int] 
lv.value = calc3 
lv.value = calc4 
var result = lv.value + 1 /* prints "calc4 */ 
+0

1番目のオプション – paradigmatic

+2

のためにこれは、それは怠惰の「キャッシング」の性質を捕捉しないため、正しい解決策ではありません。私。 lv.valueを評価するたびに、関数が再実行されます(この例では、何度も繰り返し印刷されます)。 –

1



別の解決策は、セッターとあなたのための怠惰な動作を実装するgetterメソッドとラッパーオブジェクトを使用して、次のとおりです。

class Foo { 
    private[this] var _field: String = _ 
    def field = { 
    if(_field == null) { 
     _field = "foo" // calc here 
    } 
    _field 
    } 

    def field_=(str: String) { 
    _field = str 
    } 
} 

scala> val x = new Foo 
x: Foo = [email protected] 

scala> x.field 
res2: String = foo 

scala> x.field = "bar" 
x.field: String = bar 

scala> x.field 
res3: String = bar 

編集:これは現在の形態ではスレッドセーフではありません。

EDIT2:

MHSの第二の溶液との違いは、計算だけMHSの溶液中で、それが何度も何度も呼ばれながら、一度発生しますされていること。

0

lazy val(パスに依存するタイプで使用でき、スレッドセーフでもあります)を使用したい場合は、その定義に間接レイヤを追加できます(これまでのソリューションでは、間接指定としてvarを使用しています)。

lazy val value: Int = thunk() 
@volatile private var thunk:() => Int = .. 

thunk = ... 
thunk = ... 

もちろん、再利用したい場合は、これをクラスにカプセル化することができます。

0

私は、カスタムコンテナを構築するためのすべての提供のアドバイスをまとめました:

object LazyVar { 

    class NotInitialized extends Exception 

    case class Update[+T](update :() => T) 
    implicit def impliciţUpdate[T](update:() => T) : Update[T] = Update(update) 

    final class LazyVar[T] (initial : Option[Update[T]] = None){ 
    private[this] var cache : Option[T] = None 
    private[this] var data : Option[Update[T]] = initial 

    def put(update : Update[T]) : LazyVar[T] = this.synchronized { 
     data = Some(update) 
     this 
    } 
    def set(value : T) : LazyVar[T] = this.synchronized { 
     data = None 
     cache = Some(value) 
     this 
    } 
    def get : T = this.synchronized { data match { 
     case None => cache.getOrElse(throw new NotInitialized) 
     case Some(Update(update)) => { 
     val res = update() 
     cache = Some(res) 
     res 
     } 
    } } 

    def := (update : Update[T]) : LazyVar[T] = put(update) 
    def := (value : T) : LazyVar[T] = set(value) 
    def apply() : T = get 
    } 
    object LazyVar { 
    def apply[T](initial : Option[Update[T]] = None) = new LazyVar[T](initial) 
    def apply[T](value : T) = { 
     val res = new LazyVar[T]() 
     res.set(value) 
     res 
    } 
    } 
    implicit def geţLazy[T](lazyvar : LazyVar[T]) : T = lazyvar.get 

    object Test { 
    val getInt1 :() => Int =() => { 
     print("GetInt1") 
     1 
    } 
    val getInt2 :() => Int =() => { 
     print("GetInt2") 
     2 
    } 
    val li : LazyVar[Int] = LazyVar() 
    li := getInt1 
    li := getInt2 
    val si : Int = li 
    } 
} 
関連する問題