13

Scala は laze var の作成を許可せず、lazy val のみを作成します。それは理にかなっています。

しかし、同様の機能が必要なユースケースに出くわしました。怠惰な変数ホルダーが必要です。時間のかかるアルゴリズムによって計算されるべき値が割り当てられる場合があります。しかし、後で別の値に再割り当てされる可能性があるため、最初の値の計算をまったく呼び出したくありません。

いくつかの魔法の var 定義があると仮定した例

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

このコードは、calc1 ではなく、calc2() のみを呼び出す必要があります。

暗黙の変換と特別なコンテナー クラスを使用して、このコンテナーを作成する方法を考えています。不要なコードを書く必要のない組み込みの scala 機能があるかどうか、私は興味があります

4

5 に答える 5

7

これは機能します:

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

暗黙的であることは、広く適用できるため、少し心配です。あいまいな暗黙的に関する予期しないアプリケーションやコンパイラ エラーが発生する可能性があります。



もう 1 つの解決策は、遅延動作を実装するセッターとゲッター メソッドを備えたラッパー オブジェクトを使用することです。

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 */
于 2012-07-02T07:58:38.750 に答える
2

カスタム コンテナを構築するために提供されたすべてのアドバイスをまとめました。

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
  }
}
于 2012-07-02T21:13:34.287 に答える
2

コンパイラを自分で動作させて、次のように実行するだけです。

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 の 2 番目のソリューションとの違いは、計算が 1 回だけ行われるのに対し、mhs のソリューションでは何度も何度も呼び出されることです。

于 2012-07-02T08:17:38.007 に答える
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
于 2012-07-02T07:58:11.130 に答える
1

a を引き続き使用したい場合lazy val(パス依存の型で使用でき、スレッドセーフです)、その定義に間接レイヤーを追加できます (以前のソリューションではvars を間接として使用していました)。

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

thunk = ...
thunk = ...

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

于 2012-07-02T15:37:53.517 に答える