我正在寻找一种方法,让类的行为就像case类一样,但是它们会自动散列为hash.
实现整数列表的一种方法是:
import scala.collection.mutable.{Map=>MutableMap}
sealed abstract class List
class Cons(val head: Int, val tail: List) extends List
case object Nil extends List
object Cons {
val cache : MutableMap[(Int,List),Cons] = MutableMap.empty
def apply(head : Int, tail : List) = cache.getOrElse((head,tail), {
val newCons = new Cons(head, tail)
cache((head,tail)) = newCons
newCons
})
def unapply(lst : List) : Option[(Int,List)] = {
if (lst != null && lst.isInstanceOf[Cons]) {
val asCons = lst.asInstanceOf[Cons]
Some((asCons.head, asCons.tail))
} else None
}
}
Run Code Online (Sandbox Code Playgroud)
而且,例如,同时
scala> (5 :: 4 :: scala.Nil) eq (5 :: 4 :: scala.Nil)
resN: Boolean = false
Run Code Online (Sandbox Code Playgroud)
我们得到
scala> Cons(5, Cons(4, Nil)) eq Cons(5, Cons(4, Nil))
resN: Boolean = true
Run Code Online (Sandbox Code Playgroud)
现在我正在寻找的是实现这一目标的通用方法(或类似的东西).理想情况下,我不想输入更多:
class Cons(val head : Int, val tail : List) extends List with HashConsed2[Int,List]
Run Code Online (Sandbox Code Playgroud)
(或类似的).有人可以提出一些类型系统伏都教来帮助我,还是我必须等待宏语言可用?
您可以为 N 定义一些特征,即: 、等InternableN[Arg1, Arg2, ..., ResultType]的参数数量。这些特征定义缓存本身、方法以及我们想要劫持的方法。apply()Internable1[A,Z]Internable2[A,B,Z]intern()apply
我们必须定义一个特征(或抽象类)来确保你的InternableN特征确实有一个 apply 方法可以被重写,我们称之为Applyable。
trait Applyable1[A, Z] {
def apply(a: A): Z
}
trait Internable1[A, Z] extends Applyable1[A, Z] {
private[this] val cache = WeakHashMap[(A), Z]()
private[this] def intern(args: (A))(builder: => Z) = {
cache.getOrElse(args, {
val newObj = builder
cache(args) = newObj
newObj
})
}
abstract override def apply(arg: A) = {
println("Internable1: hijacking apply")
intern(arg) { super.apply(arg) }
}
}
Run Code Online (Sandbox Code Playgroud)
您的类的伴随对象必须是ApplyableN使用InternableN. 在伴生对象中直接定义 apply 是行不通的。
// class with one apply arg
abstract class SomeClassCompanion extends Applyable1[Int, SomeClass] {
def apply(value: Int): SomeClass = {
println("original apply")
new SomeClass(value)
}
}
class SomeClass(val value: Int)
object SomeClass extends SomeClassCompanion with Internable1[Int, SomeClass]
Run Code Online (Sandbox Code Playgroud)
这样做的一个好处是,不需要修改原始申请来适应实习。它只创建实例,并且仅在需要创建实例时调用。
整件事也可以(并且应该)为具有多个参数的类定义。对于两个参数的情况:
trait Applyable2[A, B, Z] {
def apply(a: A, b: B): Z
}
trait Internable2[A, B, Z] extends Applyable2[A, B, Z] {
private[this] val cache = WeakHashMap[(A, B), Z]()
private[this] def intern(args: (A, B))(builder: => Z) = {
cache.getOrElse(args, {
val newObj = builder
cache(args) = newObj
newObj
})
}
abstract override def apply(a: A, b: B) = {
println("Internable2: hijacking apply")
intern((a, b)) { super.apply(a, b) }
}
}
// class with two apply arg
abstract class AnotherClassCompanion extends Applyable2[String, String, AnotherClass] {
def apply(one: String, two: String): AnotherClass = {
println("original apply")
new AnotherClass(one, two)
}
}
class AnotherClass(val one: String, val two: String)
object AnotherClass extends AnotherClassCompanion with Internable2[String, String, AnotherClass]
Run Code Online (Sandbox Code Playgroud)
交互表明 Internables 的 apply 方法先于原始方法执行,apply()而原始方法仅在需要时才执行。
scala> import SomeClass._
import SomeClass._
scala> SomeClass(1)
Internable1: hijacking apply
original apply
res0: SomeClass = SomeClass@2e239525
scala> import AnotherClass._
import AnotherClass._
scala> AnotherClass("earthling", "greetings")
Internable2: hijacking apply
original apply
res1: AnotherClass = AnotherClass@329b5c95
scala> AnotherClass("earthling", "greetings")
Internable2: hijacking apply
res2: AnotherClass = AnotherClass@329b5c95
Run Code Online (Sandbox Code Playgroud)
我选择使用 WeakHashMap,这样一旦在其他地方不再引用它们,驻留缓存就不会阻止对驻留实例进行垃圾回收。
代码可以作为 Github gist整齐地提供。
| 归档时间: |
|
| 查看次数: |
741 次 |
| 最近记录: |