2012-07-02 16 views
7

Scala no permite crear laze vars, solo flojos vals. Tiene sentido.make azy var in scala

Pero he tropezado con el caso de uso, donde me gustaría tener una capacidad similar. Necesito un titular variable perezoso. Se le puede asignar un valor que debe calcularse mediante un algoritmo que consume mucho tiempo. Pero luego puede ser reasignado a otro valor y me gustaría no llamar el primer cálculo de valor en absoluto.

Ejemplo suponiendo que haya alguna definición magia var

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

Esta pieza de código sólo debe llamar CALC2(), no CALC1

tengo una idea de cómo puedo escribir este recipiente con conversiones implícitas y y clase de contenedor especial. Estoy curiosidades si hay alguna característica Scala integrado que no requiere a escribir código innecesario

Respuesta

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

Esto funciona:

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

Tener las preocupaciones implícitas me ligeramente debido a que es ampliamente aplicable, que podría conducir a aplicaciones inesperadas o errores del compilador sobre implícitos ambiguos.



Otra solución es utilizar un objeto envolvente con un regulador y un método getter que implementan el comportamiento perezoso para usted:

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 por la segunda opción – paradigmatic

+2

Esto no es una solución correcta, ya que no captura la naturaleza "caching" de un perezoso. Es decir. cada vez que evalúe lv.value, la función se volverá a ejecutar (en este ejemplo, se imprimirá una y otra vez). –

1

Simplemente podría hacer los compiladores de obras de ti mismo y hacer algo como esto:

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 

corregir: Esto no es seguro para el hilo en sus formas actuales!

Edit2:

La diferencia con la segunda solución de MHS es decir, que el cálculo se suceden solamente una vez, mientras que en la solución de MHS se le llama una y otra vez.

0

Si desea seguir utilizando un lazy val (se puede usar en tipos dependientes de ruta y es seguro para subprocesos), puede agregar una capa de indirección en su definición (las soluciones anteriores usan var s como direccionamiento indirecto):

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

thunk = ... 
thunk = ... 

usted puede encapsular esto en una clase si desea volver a utilizarlo, por supuesto.

0

he resumido todos los consejos proporcionados para la construcción de contenedor personalizado:

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