Расширение классов коллекции с помощью дополнительных полей в Scala - PullRequest
2 голосов
/ 20 сентября 2011

Я хочу создать класс, который представляет собой коллекцию с дополнительным полем.Тем не менее, я продолжаю сталкиваться с проблемами и мне интересно, как лучше всего это реализовать.Я пытался следовать шаблону, приведенному в книге 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.Фактически, должно быть невозможно построить FieldSequence, просто из последовательности типа ST.Это лучшее, что я могу сделать, чтобы выбросить здесь исключение?

Ответы [ 3 ]

0 голосов
/ 21 сентября 2011

Я не уверен, что согласен с Ландеи насчет flatMap и map.Если вы замените это на выдачу исключения, как это, большинство операций должно работать.

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

Из того, что я вижу в TraversableLike, map и flatMap, а в большинстве других используется *Версия 1009 *.Так что для понимания вроде бы работа.Также кажется, что он должен следовать законам Монады (поле просто перенесено через).

Учитывая код, который вы имеете, вы можете сделать это:

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)
  }
0 голосов
/ 22 сентября 2011

В конце концов, мой ответ был очень похож на ответ в на предыдущий вопрос . Разница с этим вопросом и моим оригиналом и ответом незначительна, но в основном допускает все, что имеет последовательность к быть последовательность.

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
0 голосов
/ 20 сентября 2011

Тогда ваш класс не удовлетворяет требованиям быть Seq, и такие методы, как flatMap (и, следовательно, для понимания), не могут работать для него.

...