2017-11-06 17 views
1

私は、取引されるすべての主要通貨を含む基本的な列挙型通貨を持っています。 EURUSDJPYなどです。このコードは、1回書くか生成することができます。しかし、私はまた、すべての通貨ペアの組み合わせに対して強力な列挙型を持っていたいと思います。 EURCHFUSDCHFなどScalaには、このような派生した列挙型を動的に構築するための規定がありますか?私も外からのいくつかのスクリプトジェネレータでそれを行うことができます...しかし、それは可能かどうか疑問に思う。ScalaでEnum型を動的に作成する方法は?

object Ccy extends Enumeration { 
    type Type = Value 
    val USD = Value("USD") 
    val CHF = Value("CHF") 
    val EUR = Value("EUR") 
    val GBP = Value("GBP") 
    val JPY = Value("JPY") 
} 

object CcyPair extends Enumeration { 
    type Type = Value 
    // ??? Ccy.values.toSeq.combinations(2) ... 
} 

これは私のソリューションを実装したリファレンスとして受け入れ答えを使用してUPDATE

import scala.language.dynamics 

object CcyPair extends Enumeration with Dynamic { 
    type Type = Value 
    /* 
    * contains all currency combinations including the symmetric AB and BA 
    */ 
    private val byCcy: Map[(Ccy.Value, Ccy.Value), Value] = 
    Ccy.values.toSeq.combinations(2).map { case Seq(c1, c2) => 
     Seq(
     (c1, c2) -> Value(c1.toString + c2.toString), 
     (c2, c1) -> Value(c2.toString + c1.toString) 
    ) 
    }.flatten.toMap 
    /** 
    * reverse lookup to find currencies by currency pair, needed to find 
    * the base and risk components. 
    */ 
    private val revByCcy = byCcy.toSeq.map { case (((ccyRisk, ccyBase), ccyPair)) => 
    ccyPair -> (ccyRisk, ccyBase) 
    }.toMap 

    def apply(ccy1: Ccy.Value, ccy2: Ccy.Value): Value = { 
    assert(ccy1 != ccy2, "currencies should be different") 
    byCcy((ccy1, ccy2)) 
    } 

    implicit class DecoratedCcyPair(ccyPair: CcyPair.Type) { 
    def base: Ccy.Type = { 
     revByCcy(ccyPair)._1 
    } 

    def risk: Ccy.Type = { 
     revByCcy(ccyPair)._2 
    } 

    def name: String = ccyPair.toString() 
    } 

    def selectDynamic(ccyPair: String): Value = withName(ccyPair) 
} 

と同様に、私は物事を行うことができます:Scalaの中に魔法はありません

val ccyPair = CcyPair.EURUSD 
// or 
val ccyPair = CcyPair(Ccy.EUR, Ccy.USD) 

// and then do 
println(ccyPair.name) 
// and extract their parts like: 
// print the base currency of the pair i.e. EUR 
println(CcyPair.EURUSD.base) 
// print the risk currency of the pair i.e. USD 
println(CcyPair.EURUSD.risk) 

答えて

3

Enumeration。内部のValue関数の呼び出しは、Enumerationの内部変更可能な構造にいくつかの変更を加えるだけです。したがって、通貨ペアごとにValueに電話するだけです。次のコードは動作します:

object CcyPair1 extends Enumeration { 
    Ccy.values.toSeq.combinations(2).foreach { 
    case Seq(c1, c2) => 
     Value(c1.toString + c2.toString) 
    } 
} 

これは非常にうまく動作しません。値には、withNameまたはvaluesの関数を使用してのみアクセスできます。

scala> CcyPair1.withName("USDEUR") 
res20: CcyPair1.Value = USDEUR 

しかし、それは、例えば、この定義を拡張するためにCcy.Valueの一対のCcyPair.Valueを取得できるようにする、またはDynamicでオブジェクトフィールドによるアクセスを許可するために、またはあなたが必要とするその他の機能を提供することが可能です。

import scala.language.dynamics 

object CcyPair2 extends Enumeration with Dynamic { 
    val byCcy: Map[(Ccy.Value, Ccy.Value), Value] = 
    Ccy.values.toSeq.combinations(2).map { 
     case Seq(c1, c2) => 
     (c1, c2) -> Value(c1.toString + c2.toString) 
    }.toMap 

    def forCcy(ccy1: Ccy.Value, ccy2: Ccy.Value): Value = { 
    assert(ccy1 != ccy2, "currencies should be different") 
    if (ccy1 < ccy2) byCcy((ccy1, ccy2)) 
    else byCcy((ccy2, ccy1)) 
    } 

    def selectDynamic(pairName: String): Value = 
    withName(pairName) 
} 

この定義は、もう少し便利です。

scala> CcyPair2.forCcy(Ccy.USD, Ccy.EUR) 
res2: CcyPair2.Value = USDEUR 

scala> CcyPair2.forCcy(Ccy.EUR, Ccy.USD) 
res3: CcyPair2.Value = USDEUR 

scala> CcyPair2.USDCHF 
res4: CcyPair2.Value = USDCHF 
+0

うわー、素晴らしい答え!ありがとうございました! –

+0

あなたの答えに 'CcyPair2.USDCHF'をして魔法のように定義されていますが、私にとっては存在しません... –

+0

@GiovanniAzuaはい、それは' CcyPair2'の定義が 'Dynamic'を拡張しているからです。 'selectDynamic'がオーバーライドされています。詳細については、https://stackoverflow.com/questions/15799811/how-does-type-dynamic-work-and-how-to-use-itをご覧ください。コンパイル時ではなくランタイムエラーが発生するため、ここで使用することはお勧めできません。例として示しました。 – Kolmar

関連する問題