如何使用成员值扩展Scala集合?

时间:2015-02-10 16:47:16

标签: scala

说我有以下数据结构:

case class Timestamped[CC[M] < Seq[M]](elems : CC, timestamp : String)

所以它本质上是一个带有属性的序列 - 一个时间戳 - 附加到它上面。这很好用,我可以使用语法

创建新实例
val t = Timestamped(Seq(1,2,3,4),"2014-02-25")
t.elems.head // 1
t.timestamp  // "2014-05-25"

语法很笨拙,而我希望能够做到这样的事情:

Timestamped(1,2,3,4)("2014-02-25")
t.head      // 1
t.timestamp // "2014-05-25"

时间戳只是Seq及其实施SeqLike的扩展,只有一个属性val timestamp : String

这似乎很容易;只需使用带有mixin Seq的{​​{1}}即可。但我无法弄清楚如何创建构造函数。 我的问题是:如何在随播对象中创建一个构造函数,创建一个带有额外成员值的序列?签名如下:

TimestampMixin { val timestamp : String }

你会发现它不是直截了当的;集合使用object Timestamped { def apply(elems: M*)(timestamp : String) : Seq[M] with TimestampMixin = ??? } 来实例化自己,所以我不能简单地调用构造函数覆盖一些Builder s。

2 个答案:

答案 0 :(得分:2)

Scala集合是一个非常复杂的结构。扩展Seq需要实施applylengthiterator方法。最后,您可能最终会复制ListSet或其他内容的现有代码。你也可能不得不为你的收藏担心CanBuildFrom,如果你只是想添加一个字段,我认为这不值得。

相反,请考虑从Timestamped类型到Seq的隐式转换。

case class Timestamped[A](elems: Seq[A])(timestamp: String)

object Timestamped {
    implicit def toSeq[A](ts: Timestamped[A]): Seq[A] = ts.elems
}

现在,每当我尝试从Seq调用方法时,编译器都会隐式地将Timestamped转换为Seq,我们可以正常进行。

scala> val ts = Timestamped(List(1,2,3,4))("1/2/34")
ts: Timestamped[Int] = Timestamped(List(1, 2, 3, 4))

scala> ts.filter(_ > 2)
res18: Seq[Int] = List(3, 4)

这里有一个主要的缺点,就是在对原始Seq执行操作后,我们现在仍然遇到Timestamped

答案 1 :(得分:0)

走另一条路...延伸Seq,它只有3个抽象成员:

  case class Stamped[T](elems: Seq[T], stamp: Long) extends Seq[T] {
    override def apply(i: Int) = elems.apply(i)
    override def iterator = elems.iterator
    override def length = elems.length
  }

  val x = Stamped(List(10,20,30), 15L)

  println(x.head)              // 10
  println(x.timeStamp)         // 15
  println(x.map { _ * 10})     // List(100, 200, 300)
  println(x.filter { _ > 20})  // List(30)

请记住,只有Seq对您的用例足够具体,这才有效,如果您以后发现需要更复杂的收集行为,这可能会变得站不住脚。

编辑:添加了一个更接近您尝试创建的签名的版本。不确定这是否对您有所帮助:

  case class Stamped[T](elems: T*)(stamp: Long) extends Seq[T] {
    def timeStamp = stamp
    override def apply(i: Int) = elems.apply(i)
    override def iterator = elems.iterator
    override def length = elems.length
  }  

  val x = Stamped(10,20,30)(15L)

  println(x.head)              // 10
  println(x.timeStamp)         // 15
  println(x.map { _ * 10})     // List(100, 200, 300)
  println(x.filter { _ > 20})  // List(30)

elems最终将成为一般创建的WrappedArray