2011-12-31 34 views
10

Tôi đang tìm cách để có các lớp hoạt động giống như các lớp chữ thường, nhưng tự động là hash consed.Tự động băm lớp trường hợp được phân bổ

Một cách để đạt được điều này cho các danh sách số nguyên sẽ là:

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

Và, ví dụ, trong khi

scala> (5 :: 4 :: scala.Nil) eq (5 :: 4 :: scala.Nil) 
resN: Boolean = false 

chúng tôi nhận

scala> Cons(5, Cons(4, Nil)) eq Cons(5, Cons(4, Nil)) 
resN: Boolean = true 

Bây giờ những gì tôi đang tìm kiếm cho là chung cách để đạt được điều này (hoặc một cái gì đó rất giống nhau). Lý tưởng nhất, tôi không muốn phải nhập nhiều hơn:

class Cons(val head : Int, val tail : List) extends List with HashConsed2[Int,List] 

(hoặc tương tự). Ai đó có thể đến với một số loại voodoo hệ thống để giúp tôi, hoặc tôi sẽ phải chờ cho ngôn ngữ macro có sẵn?

Trả lời

3

Bạn có thể định nghĩa một vài InternableN[Arg1, Arg2, ..., ResultType] đặc điểm cho N là số đối số cho apply(): Internable1[A,Z], Internable2[A,B,Z], v.v. Những đặc điểm này tự xác định bộ nhớ cache, phương pháp intern() và phương pháp apply mà chúng tôi muốn hijack.

Chúng tôi sẽ phải xác định một đặc điểm (hoặc một lớp trừu tượng) để đảm bảo các đặc điểm InternableN của bạn rằng thực sự có một phương pháp áp dụng để được ghi đè lên, hãy gọi nó là 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) } 
    } 
} 

Đối tượng đồng hành của lớp học của bạn sẽ cần phải là một mixin của một lớp bê tông thực hiện ApplyableN với InternableN. Nó sẽ không hoạt động để áp dụng trực tiếp được xác định trong đối tượng đồng hành của bạn.

// 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] 

Một điều tốt về việc áp dụng ban đầu này không cần phải sửa đổi để phục vụ cho thực tập. Nó chỉ tạo ra các cá thể và chỉ được gọi khi chúng cần được tạo ra.

Toàn bộ điều có thể (và nên) cũng được xác định cho các lớp có nhiều đối số. Đối với trường hợp hai đối số:

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] 

Sự tương tác cho thấy Internables' áp dụng phương pháp thực hiện trước thời điểm ban đầu apply() mà chỉ được thực hiện nếu cần thiết.

scala> import SomeClass._ 
import SomeClass._ 

scala> SomeClass(1) 
Internable1: hijacking apply 
original apply 
res0: SomeClass = [email protected] 

scala> import AnotherClass._ 
import AnotherClass._ 

scala> AnotherClass("earthling", "greetings") 
Internable2: hijacking apply 
original apply 
res1: AnotherClass = [email protected] 

scala> AnotherClass("earthling", "greetings") 
Internable2: hijacking apply 
res2: AnotherClass = [email protected] 

Tôi chọn sử dụng WeakHashMap để bộ đệm ẩn không ngăn chặn việc thu gom rác của các cá thể thực tập khi chúng không còn được tham chiếu ở nơi khác.

Mã gọn gàng có sẵn as a Github gist.

1

Có lẽ một chút hacky, nhưng bạn có thể thử xác định phương pháp intern() riêng của bạn, giống như Java String đã:

import scala.collection.mutable.{Map=>MutableMap} 

object HashConsed { 
    val cache: MutableMap[(Class[_],Int), HashConsed] = MutableMap.empty 
} 

trait HashConsed { 
    def intern(): HashConsed = 
    HashConsed.cache.getOrElse((getClass, hashCode), { 
     HashConsed.cache((getClass, hashCode)) = this 
     this 
    }) 
} 

case class Foo(bar: Int, baz: String) extends HashConsed 

val foo1 = Foo(1, "one").intern() 
val foo2 = Foo(1, "one").intern() 

println(foo1 == foo2) // true 
println(foo1 eq foo2) // true 
Các vấn đề liên quan