在scala中创建一个惰性变量
Scala不允许创建laze变量,只允许创建lazy VAL。这是有道理的 但我遇到了一个用例,我希望在这个用例中有类似的功能。我需要一个惰性变量保持器。可以为其分配一个值,该值应通过耗时的算法进行计算。但它以后可能会被重新分配到另一个值,我不想调用第一个值计算 假设存在一些神奇的var定义的示例在scala中创建一个惰性变量,scala,lazy-evaluation,Scala,Lazy Evaluation,Scala不允许创建laze变量,只允许创建lazy VAL。这是有道理的 但我遇到了一个用例,我希望在这个用例中有类似的功能。我需要一个惰性变量保持器。可以为其分配一个值,该值应通过耗时的算法进行计算。但它以后可能会被重新分配到另一个值,我不想调用第一个值计算 假设存在一些神奇的var定义的示例 lazy var value : Int = _ val calc1 : () => Int = ... // some calculation val calc2 : () => Int
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
我知道如何使用隐式转换和特殊容器类编写这个容器。我很好奇是否有任何嵌入式scala功能不需要我编写不必要的代码这可以:
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
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()
拥有隐式让我有点担心,因为它是广泛适用的,这可能会导致有关不明确隐式的意外应用程序或编译器错误
另一种解决方案是使用包装器对象和setter和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 */
您只需自己完成编译器的工作,然后执行以下操作:
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 = Foo@11ba3c1f
scala> x.field
res2: String = foo
scala> x.field = "bar"
x.field: String = bar
scala> x.field
res3: String = bar
编辑:这不是当前形式的线程安全
编辑2:
与mhs的第二种解决方案不同的是,计算只会发生一次,而在mhs的解决方案中,它会被反复调用。如果您想继续使用
惰性val
(它可以用于路径相关类型,并且是线程安全的),可以在其定义中添加一层间接寻址(以前的解决方案使用var
s作为间接寻址):
当然,如果您想重用它,可以将其封装在一个类中。我总结了为构建自定义容器提供的所有建议:
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
}
}
这不是一个正确的解决方案,因为它没有捕获惰性的“缓存”特性。也就是说,每次计算lv.value时,函数都会重新执行(在本例中,它会一次又一次地打印)。volatile的使用非常好
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
}
}