我正在寻找一个基本上是带有额外字段的集合的类。但是,我一直遇到问题,我想知道实现这个问题的最佳方法是什么。我试图遵循Scala书中给出的模式。 E.g。
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方法。但是在这些情况下,这种方法毫无意义,因为构建FieldSequence需要一个(FT类型)字段。实际上,简单地从ST类型的序列构造FieldSequence应该是不可能的。我能在这里抛出异常是最好的吗?
答案 0 :(得分:0)
然后,您的课程不符合Seq
的要求,flatMap
等方法(以及因此的理解)也不适用于此。
答案 1 :(得分:0)
我不确定我同意Landei关于flatMap
和map
的问题。如果你替换抛出这样的异常,大多数操作都应该有效。
def apply(): Builder[ST,FieldSequence[FT,ST]] = sys.error("unsupported")
我在TraversableLike
,map
和flatMap
中看到的内容与其他大多数内容都使用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
起作用,您需要实现apply()方法。我怀疑你可以使用field
def 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)
}
答案 2 :(得分: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] = $anon$1@57f5de73
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