2011-09-20 70 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