3

私はScalaでプロジェクトを書いています。このプロジェクトには、一連の機能と一連の構成が含まれており、どちらも拡張可能です。「拡張可能」とは、後で階層に新しい機能を追加するつもりであり、再コンパイルせずに任意の構成で動作する必要があることを意味します。次の図の機能階層は次のとおりです。

trait Feature {
    def apply(board: Board)
}

class Foo extends Feature {
    def apply(board: Board) {
        println(board formatted "Foo: %s")
    }
}

class Bar extends Feature {
    def apply(board: Board) {
        println(board formatted "Bar: %s")
    }
}

構成は基本的にBoard、各 の初期機能数を含む、 の多くのパラメータを定義するだけFeatureです。実行時に構成を作成するには、いくつかの可能な戦略があります: 定義済み、ランダム、ユーザー提供の値など。理論的には、次のようなものを書きたいと思っています (有効な Scala コードではありません!)

abstract class Config(val param: Int) {
    val ConfigParameter: Int
    def featureCount[T <: Feature]: Int
}

object Config {
    def makeBasic(param: Int) = new Config(param) {
        val ConfigParameter = param
        def featureCount[Foo] = 3
        def featureCount[Bar] = 7
    }
    def makeRandom(param: Int) = new Config(param) { ... }
    def makeWithUserValues(param: Int, ...) = new Config(param) { ... }
    def makeByStandardISO1234567(param: Int) = new Config(param) { ... }
}

class Board(val config: Config) { ... }

明らかに、コンパイルされません。私の質問は、この拡張可能なシステムを Scala で表現する最良の方法は何ですか? のようなものはいつでも含めることができますがMap[Class, Int]、タイプ セーフではありません。プログラマは、 ではないConfigクラスを に挿入できます。では、Scala の型システムで、異なるキーが異なるサブタイプであるようなものを表す方法はありますか? あるいは、このすべての動作を階層に移動する方法があるのでしょうか?MapFeatureMap[Class[T <: Feature], Int]MapFeatureFeature

ありがとうございました。

4

3 に答える 3

2

ClassManifest( Scala 2.10 では) を使用ClassTagして、マップ ソリューションを改善できます。

package object features {
  type FeatureMap = Map[Class[_ <: Feature], Int]
}

abstract class Config(val param: Int) {
    def ConfigParameter: Int
    def featureMap: FeatureMap
    def featureCount[T<:Feature]( implicit man: ClassManifest[T] ): Int = 
      featureMap( man.erasure )
}

object Config {
    def makeBasic(param: Int) = new Config(param) {
        val ConfigParameter = param
        lazy val featureMap: FeatureMap = Map(
            classOf[Foo] -> 3,
            classOf[Bar] -> 7
        )
    }
}

コンパイラを呼び出すfeatureCountたびに、括弧内に渡された型に適した classManifest が使用されます。erasureメソッドは、対応するクラスを返します。

注意: 抽象値は避けてください。迷惑な影響があり、バイナリ互換性が損なわれる可能性があります。

于 2012-12-30T09:23:57.967 に答える
0

私はMapベースのソリューションを見つけました(おそらくそれは最もエレガントなものではありませんが):

package object features {
    type FeatureMap = Map[Class[_ <: Feature], Int]
}

abstract class Config(val param: Int) {
    val ConfigParameter: Int
    val FeatureCount: FeatureMap
}

object Config {
    def makeBasic(param: Int) = new Config(param) {
        val ConfigParameter = param
        lazy val FeatureCount: FeatureMap = Map(
            classOf[Foo] -> 3,
            classOf[Bar] -> 7
        )
    }
}
于 2012-12-30T07:44:00.987 に答える
0

の各キーの特定のサブタイプを知るためにマップが必要Featureですか? たとえば、次のように書きます。

trait Feature { def apply(board: Board) }

case object Foo extends Feature {
  def apply(board: Board) { printf("Foo: %s", board) }
}

case object Bar extends Feature {
  def apply(board: Board) { printf("Bar: %s", board) }
}

// ...
val featureCount: Map[Feature, Int] = Map(Foo -> 3, Bar -> 7)

featureCount(Foo)これで、 、 などと書くことができます。オブジェクト以外の値が侵入できないことを確認したいかのようfeatureCount.get(Bar)に、マップを入力することもできます。Map[Feature with Singleton, Int]

別の方法として、型をキーにしたマップが本当に必要な場合は、 Shapelessを使用して次のようにすることができます。

trait Feature

case class Foo(count: Int) extends Feature
case class Bar(count: Int) extends Feature

import shapeless._

object featureWithCount extends Poly0 {
  implicit def foo = at(Foo(3))
  implicit def bar = at(Bar(7))
}

その後:

scala> featureWithCount[Foo]
res0: Foo = Foo(3)

scala> featureWithCount[Bar]
res1: Bar = Bar(7)

ただし、ここで多くのことを購入できるかどうかはわかりません。

于 2012-12-30T14:45:08.180 に答える