2012-07-02 64 views
7

Scala不允許創建lazer變量,只有懶惰的vals。有道理。在scala中創建一個懶惰變量

但我碰到用例,我想要有類似的功能。我需要一個懶惰的變量持有者。它可能被分配一個值,應該通過耗時的算法來計算。但它可能後來重新分配給另一個值,我不想調用第一個值計算。

示例假設有一些神奇的VAR定義

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

這段代碼只能調用CALC2(),不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

這適用於任何嵌入式斯卡拉特點:

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

稍微具有隱含的擔憂我,因爲它是廣泛適用,這可能會導致意外的應用程序或編譯器有關含糊含義的錯誤。



另一種解決方案是使用與二傳手實現你的懶惰行爲的包裝物和getter方法:

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

你可以簡單地做編譯器作品自己和做某事是這樣的:

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 S作爲一個間接):

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 
    } 
}