jsフレームワークのVueには算出プロパティという便利機能がありまして、
こんな感じの機能をScalaでも使いたかったので実装してみました。
Scalaは雰囲気で使ってるのでよくわかりません。
Vueの算出プロパティって一般的な計算機用語でなんていうんですかね?
スマートゲッターとかメモ化ゲッターじゃなさそう?
以下本体。
Computed.scala
import scala.collection.mutable
class Computed[T](private var func: Option[Computed[_]] => T) {
private var cache: Option[T] = None
private var value: Option[T] = None
private val parents = new mutable.WeakHashMap[Computed[_], Boolean]
private var listeners = Seq.empty[Unit => Unit]
def get(implicit computed: Option[Computed[_]]): T = {
computed.foreach(parents(_) = true)
if (needsUpdate) {
cache = Some(func(Some(this)))
}
cache.get
}
def :=(func: Option[Computed[_]] => T): Unit = {
if (func != this.func) {
value = None
this.func = func
modified()
}
}
def ::=(value: T): Unit = {
val optional = Option(value)
if (optional != this.value) {
this.value = optional
:=(_ => value)
}
}
def needsUpdate: Boolean = cache.isEmpty
def modified(): Seq[Unit => Unit] = {
cache = None
val list = mutable.ListBuffer.empty[Unit => Unit]
list ++= listeners
parents.keys.foreach(list ++= _.modified())
parents.clear()
list
}
def addOnChangeListener(listener: Unit => Unit): Unit = {
listeners :+= listener
listeners = listeners.distinct
}
def clearListener(): Unit = {
listeners = Seq()
}
}
object Computed {
def apply[T](func: Option[Computed[_]] => T): Computed[T] = new Computed(func)
def value[T](value: T): Computed[T] = {
val computed = new Computed(_ => value)
computed.value = Option(value)
computed
}
}
以下便利ImplicitConversion。
Extensions.scala
import scala.language.implicitConversions
object Extensions {
implicit def computedToValue[R](computed: Computed[R])(implicit parent: Option[Computed[_]] = None): R = computed.get
implicit def $v[T](value: T): Computed[T] = Computed.value(value)
}
以下使い方。
import test.{Computed => $}
import test.Extensions._
......
val value = $v("最初の値")
var count = 0
var deepCount = 0
val computed = $ { implicit c =>
count += 1
value + "です"
}
val deepComputed = $ { implicit c =>
deepCount += 1
computed + "だよ"
}
println(count) // 0
println(computed.get(None)) // 最初の値です
println(count) // 1
println(computed.get(None)) // 最初の値です
println(count) // 1
value ::= "次の値"
println(deepCount) // 0
println(deepComputed.get(None)) // 次の値ですだよ
println(deepCount) // 1
println(deepComputed.get(None)) // 次の値ですだよ
println(deepCount) // 1
println(count) // 2
println(computed.get(None)) // 次の値です
println(count) // 2
最初と更新が入って必要なときだけ計算し、それ以外はキャッシュを返してそうですね。
Scala + libGDXでスマホゲームを作っていて、画面レイアウトを計算するのに便利そうだったので作りました。