2011-09-20 71 views
2

我正在创建一个基本上是带有额外字段的集合的类。但是,我一直在遇到问题,并想知道实现这一点的最佳方式是什么。我试图按照Scala书中给出的模式。例如。在Scala中扩展带额外字段的集合类

import scala.collection.IndexedSeqLike 
import scala.collection.mutable.Builder 
import scala.collection.generic.CanBuildFrom 
import scala.collection.mutable.ArrayBuffer 

class FieldSequence[FT,ST](val field: FT, seq: IndexedSeq[ST] = Vector()) 
     extends IndexedSeq[ST] with IndexedSeqLike[ST,FieldSequence[FT,ST]] { 

    def apply(index: Int): ST = return seq(index) 
    def length = seq.length 

    override def newBuilder: Builder[ST,FieldSequence[FT,ST]] 
     = FieldSequence.newBuilder[FT,ST](field) 
} 

object FieldSequence { 

    def fromSeq[FT,ST](field: FT)(buf: IndexedSeq[ST]) 
     = new FieldSequence(field, buf) 

    def newBuilder[FT,ST](field: FT): Builder[ST,FieldSequence[FT,ST]] 
     = new ArrayBuffer mapResult(fromSeq(field)) 

    implicit def canBuildFrom[FT,ST]: 
      CanBuildFrom[FieldSequence[FT,ST], ST, FieldSequence[FT,ST]] = 
     new CanBuildFrom[FieldSequence[FT,ST], ST, FieldSequence[FT,ST]] { 
     def apply(): Builder[ST,FieldSequence[FT,ST]] 
      = newBuilder[FT,ST](_) // What goes here? 
     def apply(from: FieldSequence[FT,ST]): Builder[ST,FieldSequence[FT,ST]] 
      = from.newBuilder 
     } 
} 

问题是隐式定义的CanBuildFrom需要一个没有参数的apply方法。但是在这种情况下,这种方法是毫无意义的,因为需要一个字段(类型FT)来构造一个FieldSequence。实际上,仅仅从一个ST类型的序列中构建一个FieldSequence是不可能的。我能做些什么来抛出异常?

回答

0

然后你的班级不符合要求是Seq,并且像flatMap(因此为理解)的方法不能为它工作。

+0

我想尽可能多的,但我还是留下了如何实现一些东西,做这个问题。也许FieldSequence的抽象方法的默认值? –

+0

你真的需要继承吗?或者是组合性够好,有一个包装类负责Seq的特殊初始化?包装器仍然可以实现像“Traversable”这样较弱的东西,并委托它。 – Landei

+0

我不确定你是否会将我的解决方案算作继承或组合。我不知道这些术语如何与Scala相比,而不是更传统的语言。我认为你正在形成一种p pattern模式,但是由于我控制了班级,所以我认为没有必要。 –

0

我不确定我是否同意Landei关于flatMapmap。如果用抛出像这样的异常进行替换,大部分操作都应该起作用。

def apply(): Builder[ST,FieldSequence[FT,ST]] = sys.error("unsupported") 

从我可以在TraversableLikemapflatMap和其他大多数的人看到使用apply(repr)版本。所以理解似乎很有用。它也觉得它应该遵循Monad法则(该领域只是进行交叉)。

鉴于你的代码,你可以这样做:

scala> val fs = FieldSequence.fromSeq("str")(Vector(1,2)) 
fs: FieldSequence[java.lang.String,Int] = FieldSequence(1, 2) 

scala> fs.map(1 + _) 
res3: FieldSequence[java.lang.String,Int] = FieldSequence(2, 3) 

scala> val fs2 = FieldSequence.fromSeq("str1")(Vector(10,20)) 
fs2: FieldSequence[java.lang.String,Int] = FieldSequence(10, 20) 

scala> for (x <- fs if x > 0; y <- fs2) yield (x + y) 
res5: FieldSequence[java.lang.String,Int] = FieldSequence(11, 21, 12, 22) 

什么不工作如下:

scala> fs.map(_ + "!") 
// does not return a FieldSequence 

scala> List(1,2).map(1 + _)(collection.breakOut): FieldSequence[String, Int] 
java.lang.RuntimeException: unsupported 
// this is where the apply() is used 

对于breakOut工作,你将需要实现应用( ) 方法。我怀疑你可以使用一些默认值fielddef apply() = newBuilder[FT, ST](getDefault)来生成一个构建器,其中一些getDefault实现对你的用例很有意义。

的事实,fs.map(_ + "!")不保留类型,你需要修改你的签署和实施,这样编译器可以找到一个CanBuildFrom[FieldSequence[String, Int], String, FieldSequence[String, String]]

implicit def canBuildFrom[FT,ST_FROM,ST]: 
     CanBuildFrom[FieldSequence[FT,ST_FROM], ST, FieldSequence[FT,ST]] = 
    new CanBuildFrom[FieldSequence[FT,ST_FROM], ST, FieldSequence[FT,ST]] { 
    def apply(): Builder[ST,FieldSequence[FT,ST]] 
     = sys.error("unsupported") 
    def apply(from: FieldSequence[FT,ST_FROM]): Builder[ST,FieldSequence[FT,ST]] 
     = newBuilder[FT, ST](from.field) 
    } 
+0

感谢您的意见,它让我想到了一些事情,以便得到更好的答案。 –

0

到最后,我的回答很相似在a previous question。与那个问题和我的原始和答案的区别是轻微的,但基本上允许任何一个序列一个序列。

import scala.collection.SeqLike 
import scala.collection.mutable.Builder 
import scala.collection.mutable.ArrayBuffer 
import scala.collection.generic.CanBuildFrom 

trait SeqAdapter[+A, Repr[+X] <: SeqAdapter[X,Repr]] 
     extends Seq[A] with SeqLike[A,Repr[A]] { 
    val underlyingSeq: Seq[A] 
    def create[B](seq: Seq[B]): Repr[B] 

    def apply(index: Int) = underlyingSeq(index) 
    def length = underlyingSeq.length 
    def iterator = underlyingSeq.iterator 

    override protected[this] def newBuilder: Builder[A,Repr[A]] = { 
     val sac = new SeqAdapterCompanion[Repr] { 
      def createDefault[B](seq: Seq[B]) = create(seq) 
     } 
     sac.newBuilder(create) 
    } 
} 

trait SeqAdapterCompanion[Repr[+X] <: SeqAdapter[X,Repr]] { 
    def createDefault[A](seq: Seq[A]): Repr[A] 
    def fromSeq[A](creator: (Seq[A]) => Repr[A])(seq: Seq[A]) = creator(seq) 
    def newBuilder[A](creator: (Seq[A]) => Repr[A]): Builder[A,Repr[A]] = 
     new ArrayBuffer mapResult fromSeq(creator) 

    implicit def canBuildFrom[A,B]: CanBuildFrom[Repr[A],B,Repr[B]] = 
     new CanBuildFrom[Repr[A],B,Repr[B]] { 
      def apply(): Builder[B,Repr[B]] = newBuilder(createDefault) 
      def apply(from: Repr[A]) = newBuilder(from.create) 
     } 
} 

这解决了huynhjl带来的所有问题。对于我最初的问题,为了将一个字段和一个序列当作一个序列来处理,现在就可以做一个简单的类。

trait Field[FT] { 
    val defaultValue: FT 

    class FieldSeq[+ST](val field: FT, val underlyingSeq: Seq[ST] = Vector()) 
      extends SeqAdapter[ST,FieldSeq] { 
     def create[B](seq: Seq[B]) = new FieldSeq[B](field, seq) 
    } 

    object FieldSeq extends SeqAdapterCompanion[FieldSeq] { 
     def createDefault[A](seq: Seq[A]): FieldSeq[A] = 
      new FieldSeq[A](defaultValue, seq) 
     override implicit def canBuildFrom[A,B] = super.canBuildFrom[A,B] 
    } 
} 

这可以测试像这样:

val StringField = new Field[String] { val defaultValue = "Default Value" } 
StringField: java.lang.Object with Field[String] = [email protected] 

val fs = new StringField.FieldSeq[Int]("str", Vector(1,2)) 
val fsfield = fs.field 
fs: StringField.FieldSeq[Int] = (1, 2) 
fsfield: String = str 

val fm = fs.map(1 + _) 
val fmfield = fm.field 
fm: StringField.FieldSeq[Int] = (2, 3) 
fmfield: String = str 

val fs2 = new StringField.FieldSeq[Int]("str1", Vector(10, 20)) 
val fs2field = fs2.field 
fs2: StringField.FieldSeq[Int] = (10, 20) 
fs2field: String = str1 

val ffor = for (x <- fs if x > 0; y <- fs2) yield (x + y) 
val fforfield = ffor.field 
ffor: StringField.FieldSeq[Int] = (11, 21, 12, 22) 
fforfield: String = str 

val smap = fs.map(_ + "!") 
val smapfield = smap.field 
smap: StringField.FieldSeq[String] = (1!, 2!) 
smapfield: String = str 

val break = List(1,2).map(1 + _)(collection.breakOut): StringField.FieldSeq[Int] 
val breakfield = break.field 
break: StringField.FieldSeq[Int] = (2, 3) 
breakfield: String = Default Value 

val x: StringField.FieldSeq[Any] = fs 
val xfield = x.field 
x: StringField.FieldSeq[Any] = (1, 2) 
xfield: String = str