受this question的启发,我想在Scala中实现Multiset。我想要MultiSet[A]
来:
A => Int
,提供每个元素的计数这是一种方法,扩展Set
:
import scala.collection.immutable.Map
import scala.collection.immutable.Set
import scala.collection.SetLike
import scala.collection.mutable.Builder
class MultiSet[A](private val counts: Map[A, Int] = Map.empty[A, Int])
extends SetLike[A, MultiSet[A]] with Set[A] {
override def +(elem: A): MultiSet[A] = {
val count = this.counts.getOrElse(elem, 0) + 1
new MultiSet(this.counts + (elem -> count))
}
override def -(elem: A): MultiSet[A] = this.counts.get(elem) match {
case None => this
case Some(1) => new MultiSet(this.counts - elem)
case Some(n) => new MultiSet(this.counts + (elem -> (n - 1)))
}
override def contains(elem: A): Boolean = this.counts.contains(elem)
override def empty: MultiSet[A] = new MultiSet[A]
override def iterator: Iterator[A] = {
for ((elem, count) <- this.counts.iterator; _ <- 1 to count) yield elem
}
override def newBuilder: Builder[A,MultiSet[A]] = new Builder[A, MultiSet[A]] {
var multiSet = empty
def +=(elem: A): this.type = {this.multiSet += elem; this}
def clear(): Unit = this.multiSet = empty
def result(): MultiSet[A] = this.multiSet
}
override def seq: MultiSet[A] = this
}
object MultiSet {
def empty[A]: MultiSet[A] = new MultiSet[A]
def apply[A](elem: A, elems: A*): MultiSet[A] = MultiSet.empty + elem ++ elems
def apply[A](elems: Seq[A]): MultiSet[A] = MultiSet.empty ++ elems
def apply[A](elem: (A, Int), elems: (A, Int)*) = new MultiSet((elem +: elems).toMap)
def apply[A](elems: Map[A, Int]): MultiSet[A] = new MultiSet(elems)
}
扩展Set
很不错,因为这意味着MultiSet
会自动获取union,difference等的定义。以下所有内容都将成立:
// add
assert(
MultiSet("X" -> 3, "Y" -> 1) + "X" ===
MultiSet("X" -> 4, "Y" -> 1))
assert(
MultiSet("X" -> 3, "Y" -> 1) + "Z" ===
MultiSet("X" -> 3, "Y" -> 1, "Z" -> 1))
// remove
assert(
MultiSet("a" -> 2, "b" -> 5) - "b" ===
MultiSet("a" -> 2, "b" -> 4))
assert(
MultiSet("a" -> 2, "b" -> 5) - "c" ===
MultiSet("a" -> 2, "b" -> 5))
// add all
assert(
MultiSet(10 -> 1, 100 -> 3) ++ MultiSet(10 -> 1, 1 -> 7) ===
MultiSet(100 -> 3, 10 -> 2, 1 -> 7))
// remove all
assert(
MultiSet("a" -> 2, "b" -> 5) -- MultiSet("a" -> 3) ===
MultiSet("b" -> 5))
但是,我必须覆盖一些继承的方法,例如union
和intersect
,因为它们会对多重集合执行错误的操作,例如以下内容不成立:
// union (takes max of values)
assert(
MultiSet(10 -> 5, 1 -> 1).union(MultiSet(10 -> 3, 1 -> 7)) ===
MultiSet(10 -> 5, 1 -> 7))
// intersection (takes min of values)
assert(
MultiSet(10 -> 5, 100 -> 3).intersect(MultiSet(10 -> 1, 1 -> 7)) ===
MultiSet(10 -> 1))
扩展Set
的另一个问题是我不能MultiSet
成为A => Int
,因为我会收到错误:illegal inheritance; class MultiSet inherits different type instances of trait Function1: A => Int and A => Boolean
。我可以通过声明一个单独的count
方法来解决这个问题,但我真的更喜欢这个类只是A => Int
。
另一种方法是从Map[A, Int]
继承,这会给我我想要的A => Int
,但是我必须定义我自己的所有++
,{{1因为在--
中,这些将在Map
对上定义,但对于多集,它们需要在(A, Int)
s上定义。
我想第三种方法是放弃A
和Set
,只需实现Map
或其他任何新的子类。
你会推荐什么?在Scala集合框架中放置Iterable
的最佳方法是什么?
答案 0 :(得分:1)
但是,我必须覆盖一些继承的方法,比如intersect,因为它们会为多重集合做错误的事情
我认为你必须咬紧牙关并做到这一点。
扩展Set的另一个问题是,我不能让MultiSet成为
A => Int
实际上,您不能使用不同的类型参数继承两次相同的特征(此处为Function1
)。实际上在这种情况下,这不仅仅是一个恼人的技术限制,但这样做实际上没有多大意义,因为在调用apply
时,无法知道要调用哪个重载:{{1 }或def apply( key: A ): Boolean
?你无法分辨,因为参数列表是相同的。
但是,您可以做的是添加从def apply( key: A ): Int
到MultiSet[A]
的隐式转换。这样,默认情况下A => Int
被视为MultiSet
(与任何集合一样),但在嵌入时可以强制转换为A => Boolean
(特别是它可以直接传递给函数)期待A => Int
函数。)
A => Int
一些测试我的REPL:
class MultiSet[A] ... {
...
def count(elem: A): Int = counts.getOrElse( elem, 0 )
}
object MultiSet {
...
implicit def toCountFunc[A]( ms: MultiSet[A] ): A => Int = {
(x: A) => ms.count( x )
}
}