【问题标题】:How to create a wrapper of List with a specific type如何创建具有特定类型的 List 包装器
【发布时间】:2011-08-05 07:37:42
【问题描述】:

我正在尝试创建具有特定类型(例如 List[Int])的 List 包装器,以便采用隐式 CanBuildFrom 参数的方法返回我的包装器实例而不是 List

一种可能的解决方案,感觉相当重量级,是:

import scala.collection._
import generic.{CanBuildFrom, SeqForwarder}
import mutable.{Builder, ListBuffer}

class MyList(list: List[Int]) extends immutable.LinearSeq[Int]
                                 with LinearSeqLike[Int, MyList]
                                 with SeqForwarder[Int] {
  override def newBuilder: Builder[Int, MyList] = MyList.newBuilder
  protected override def underlying = list
}

object MyList {
  def newBuilder: Builder[Int, MyList] =
    new ListBuffer[Int] mapResult(new MyList(_))

  implicit def canBuildFrom: CanBuildFrom[MyList, Int, MyList] = {
    new CanBuildFrom[MyList, Int, MyList] {
      def apply(from: MyList) = from.newBuilder
      def apply() = newBuilder
    }
  }
}

val l1 = new MyList(List(1,2,3))

println(l1.isInstanceOf[MyList])
println(l1.map(_ + 1).isInstanceOf[MyList])
println(l1.filter(_ == 2).isInstanceOf[MyList])

是否有更好/更简单的方法来创建这样的包装器,或者我错过了MyList 的实现中的任何重要内容?

编辑: 一个后续问题是:可以将整个包装逻辑放入ListWrapper 类或特征中,以便可以像这样实现上述MyList

class MyList extends ListWrapper[Int, MyList]

object MyList extends ListWrapperFactory[Int, MyList]

【问题讨论】:

    标签: list scala scala-2.8 scala-collections


    【解决方案1】:

    据我阅读这篇文章所知:

    http://www.scala-lang.org/docu/files/collections-api/collections-impl.html

    如果您想要过滤器/地图/等,您的解决方案是最简单的。到MyList 的所有返回实例。 newBuilder 对于像 filter 这样的操作需要 CanBuildFrom 对于像 map 这样的操作,这可能会更改集合类型。

    您在CanBuildFrom 中应该做的是:

    def apply(from: MyList) = from.newBuilder // call it on `from'
    

    确保静态类型MyList 上的map 实际上具有作为MyList 子类型的动态类型将重用相同的动态类型。

    编辑:似乎缺少了一些东西,因为这个map 返回List 的实例而不是MyList

    val l1: LinearSeq[Int] = new MyList(List(1, 2, 3))
    println(l1.map(_ + 1)) // prints List(2, 3, 4)
    

    看起来这也是链接文章中的RNA 示例的情况。如果它具有静态类型 IndexedSeq[Base] 而不是 RNA,则其上的 map 返回一个向量。

    编辑 2:看起来这是一个更普遍的问题,在 this question 中讨论过。

    【讨论】:

    • 静态类型决定了使用的CanBuildFrom
    • 事实上,你可以用 Scala 自己的集合来检查这个。尝试使用BitSetSet,这与给出的示例非常相似。
    • @Daniel 是的,确实如此。我试图在这个问题中更一般地讨论这个问题,但收效甚微:stackoverflow.com/questions/5678682/…
    【解决方案2】:

    关于我的后续问题如何通过类或特征混合包装器逻辑,这是我想出的:

    import scala.collection._
    
    trait ListWrapper[Elem, Repr <: ListWrapper[Elem, Repr]]
        extends immutable.LinearSeq[Elem]
        with LinearSeqLike[Elem, Repr]
        with generic.SeqForwarder[Elem] { self: Repr =>
    
      def wrapperCompanion: ListWrapperCompanion[Elem, Repr]
    
      override def newBuilder: mutable.Builder[Elem, Repr] =
        wrapperCompanion.newBuilder
    }
    
    trait ListWrapperCompanion[Elem, Repr <: ListWrapper[Elem, Repr]] {
      def apply(elems: TraversableOnce[Elem]): Repr
    
      def newBuilder: mutable.Builder[Elem, Repr] =
        new mutable.ListBuffer[Elem].mapResult(apply)
    
      def canBuildFromWrapper: generic.CanBuildFrom[Repr, Elem, Repr] = {
        new generic.CanBuildFrom[Repr, Elem, Repr] {
          def apply(from: Repr) = from.newBuilder
          def apply() = newBuilder
        }
      }
    }
    

    现在MyList可以通过以下方式实现:

    class MyList(val underlying: List[Int]) extends ListWrapper[Int, MyList] {
      def wrapperCompanion = MyList
    }
    
    object MyList extends ListWrapperCompanion[Int, MyList] {
      def apply(elems: TraversableOnce[Int]) = new MyList(elems.toList)
    
      implicit def canBuildFrom = canBuildFromWrapper
    }
    

    这绝对比在MyList 的定义中包含所有样板代码要好,但是要使MyList 只是List 的包装器,仍然需要编写很多代码。

    【讨论】:

    • 不要在抽象类的构造函数中传递wrapperCompanion,而是将其设为抽象def — 是的,MyList 的定义会变长,但您会为额外的字段节省空间现在在 MyList 的每个实例中都需要它。
    • 好建议。我想让MyList 的定义尽可能短,并且不太担心其中的含义。
    • 您也可以从object MyList 中删除implicit def canBuildFrom 并将trait ListWrapperCompanion 隐含。
    • 如果我这样做,当我在MyList 上调用map 时,我会得到error: ambiguous implicit values: both method canBuildFrom in object LinearSeq of type [A]scala.collection.generic.CanBuildFrom[scala.collection.immutable.LinearSeq.Coll,A,scala.collection.immutable.LinearSeq[A]] and method canBuildFrom in trait ListWrapperCompanion of type =&gt; scala.collection.generic.CanBuildFrom[this.MyList,Int,this.MyList] match expected type scala.collection.generic.CanBuildFrom[this.MyList,Int,That]
    猜你喜欢
    • 1970-01-01
    • 2018-10-08
    • 2021-10-05
    • 1970-01-01
    • 2021-06-16
    • 2016-06-16
    • 2019-07-20
    • 2016-04-07
    • 2020-12-11
    相关资源
    最近更新 更多