scala – 如何使用用户定义的相等性实现集合
发布时间:2020-12-16 09:15:16 所属栏目:安全 来源:网络整理
导读:这个问题与 this one有关:是否可以在Scala中创建一个Set-like类(意味着它扩展了Set trait),用于定义包含关系的相等性是由用户定义而不是==? 测试这个真正有效的一种方法是检查过滤器是否返回相同的集合类型. // typeclass for equalitytrait Equals[T] { d
这个问题与
this one有关:是否可以在Scala中创建一个Set-like类(意味着它扩展了Set trait),用于定义包含关系的相等性是由用户定义而不是==?
测试这个真正有效的一种方法是检查过滤器是否返回相同的集合类型. // typeclass for equality trait Equals[T] { def isEqual(t1: T,t2: T): Boolean } // an object representing plane coordinates case class Coordinate(i: Int,j: Int) // an equality saying that 2 coordinates are equal if they are on // the same horizontal line implicit def horizontalEquality: Equals[Coordinate] = new Equals[Coordinate] { def isEqual(t1: Coordinate,t2: Coordinate) = t1.i == t2.i } // we create an EqualitySet[T] where T must verify [T : Equals] val set = EqualitySet[Coordinate]() // set2 must be of type EqualitySet[Coordinate] val set2 = set.filter(_.i > 0) 解决方法
我们创建了这个解决方案,作为一个与Miles Sabin(@milessabin)进行Scala培训的团队.
import scala.collection.mutable.ListBuffer import scala.collection.generic.CanBuildFrom import scala.collection.SetLike import scala.collection.mutable.Builder /** * we extend Set[T] to provide the Set-like interface * we extends SetLike[T,EqualitySet[T]] to specify that Set methods will return * instances of type EqualitySet (and not simply Set) */ trait EqualitySet[T] extends Set[T] with SetLike[T,EqualitySet[T]] { outer => /** we need to provide an Equals[T] instance to create an EqualitySet[T] */ implicit def equality: Equals[T] /** our internal implementation as a list of elements */ protected val set = ListBuffer[T]() /** we need to implements those 4 methods */ def contains(t: T) = set.exists(equality.isEqual(_,t)) def +(t: T) = { if (!contains(t)) set += t; this } def -(t: T) = { set -= t; this } def iterator = set.iterator /** we must be able to provide an empty set with the proper equality definition */ override def empty = new EqualitySet[T] { override def equality = outer.equality } } /** * Companion object for the EqualitySet class */ object EqualitySet { /** * this implicit is absolutely necessary to be able to preserve the resulting * collection type when calling `filter` */ implicit def canBuildFrom[T] = new CanBuildFrom[EqualitySet[T],T,EqualitySet[T]] { def apply(from: EqualitySet[T]): Builder[T,EqualitySet[T]] = new Builder[T,EqualitySet[T]] { // use a ListBuffer internally to accumulate elements private val elems = ListBuffer[T]() def +=(t: T) = { if (!elems.exists(from.equality.isEqual(_,t))) elems += t this } def clear() = elems.clear // when we finish building the collection // we can return an EqualitySet with the original equality relation def result() = new EqualitySet[T] { override val set = elems override def equality = from.equality } } def apply(): Builder[T,EqualitySet[T]] = sys.error("this can't be implemented,because no equality instance is provided") } /** @return an EqualitySet for a type T having an Equals instance */ def apply[T : Equals](ts: T*) = { var set = new EqualitySet[T] { def equality = implicitly[Equals[T]] }.empty ts.foreach { t => set += t } set } } 然后,当我们使用上面的代码时,我们得到: scala> val set = EqualitySet[Coordinate](Coordinate(-1,2),Coordinate(-1,3),Coordinate(1,4)) set: java.lang.Object with test.EqualitySet[Coordinate] = Set(Coordinate(-1,2) Coordinate(1,4)) scala> val set2 = set.filter(_.i > 0) // still an EqualitySet[Coordinate] o/ */ set2: test.EqualitySet[Coordinate] = Set(Coordinate(1,4)) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |