2012-07-02 30 views
7

Scala không cho phép tạo ra các vars laze, chỉ vals lười biếng. Nó có ý nghĩa.làm cho một var lười biếng trong scala

Nhưng tôi đã gặp phải trường hợp sử dụng, nơi tôi muốn có khả năng tương tự. Tôi cần một người giữ biến lười biếng. Nó có thể được gán một giá trị cần được tính toán bằng thuật toán tốn thời gian. Nhưng sau này nó có thể được gán lại cho một giá trị khác và tôi không muốn gọi tính toán giá trị đầu tiên chút nào.

Ví dụ giả sử có một số nét diệu var

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

Đoạn mã này chỉ nên gọi calc2(), không calc1

Tôi có một ý tưởng làm thế nào tôi có thể viết chứa này với các chuyển đổi ngầm và và lớp container đặc biệt. Tôi curios nếu có bất kỳ tính năng scala nhúng mà không yêu cầu tôi viết mã không cần thiết

Trả lời

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

này hoạt động:

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

Có những lo ngại tiềm ẩn cho tôi một chút vì nó được áp dụng rộng rãi, trong đó có thể dẫn đến các ứng dụng không mong muốn hoặc lỗi trình biên dịch về các hàm ý mơ hồ.



Một giải pháp khác là sử dụng một đối tượng wrapper với một setter và một phương thức getter mà thực hiện hành vi lười biếng cho bạn:

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 cho tùy chọn thứ hai – paradigmatic

+2

Đây không phải là một giải pháp đúng vì nó không nắm bắt được "bộ nhớ đệm" bản chất của một lười biếng. I E. mỗi khi bạn đánh giá lv.value, hàm sẽ được thực hiện lại (trong ví dụ này nó sẽ in lại và lặp lại). –

1

Bạn chỉ có thể làm các nhà biên soạn công trình bản thân và làm sth như thế này:

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 

chỉnh sửa: Đây không phải là chủ đề an toàn ở dạng dòng!

edit2:

Sự khác biệt với các giải pháp thứ hai của MHS được, mà việc tính toán sẽ chỉ xảy ra một lần, trong khi trong dung dịch MHS của nó được gọi là hơn và hơn nữa.

0

Nếu bạn muốn tiếp tục sử dụng lazy val (nó có thể được sử dụng trong loại phụ thuộc vào đường dẫn và chỉ an toàn), bạn có thể thêm một lớp hướng vào định nghĩa của nó (các giải pháp trước đây sử dụng var s làm hướng dẫn):

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

thunk = ... 
thunk = ... 

Bạn có thể gói gọn trong lớp nếu bạn muốn sử dụng lại nó, tất nhiên.

0

tôi đã tóm tắt tất cả các lời khuyên được cung cấp cho việc xây dựng chứa tùy chỉnh:

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 
    } 
} 
Các vấn đề liên quan