如何在 Scala 中动态创建 Enum 类型?

Sky*_*ker 1 enums scala

我有一个基本的枚举类型货币,这将包括所有交易的主要货币如EURUSDJPY,等这段代码我可以写或产生一次。不过,我也想对所有货币对强劲枚举类型的组合如EURCHFUSDCHF等有Scala中的任何条款,让我建立这样一个动态派生枚举类型?我也可以用一些外部的脚本生成器来做……但我想知道是否有可能。

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) ...   
}
Run Code Online (Sandbox Code Playgroud)

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)
}
Run Code Online (Sandbox Code Playgroud)

然后我可以做这样的事情:

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)
Run Code Online (Sandbox Code Playgroud)

Kol*_*mar 5

Scala 的Enumeration. 对Value内部函数的调用只是对Enumeration的内部可变结构进行了一些修改。所以你只需要调用Value每对货币。以下代码将起作用:

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

不过工作起来不是很舒服。您只能通过withNamevalues函数访问这些值。

scala> CcyPair1.withName("USDEUR")
res20: CcyPair1.Value = USDEUR
Run Code Online (Sandbox Code Playgroud)

但是可以扩展此定义,例如,允许CcyPair.Value通过一对Ccy.Values进行检索,或允许通过对象字段访问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)
}
Run Code Online (Sandbox Code Playgroud)

这个定义更有用一点:

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
Run Code Online (Sandbox Code Playgroud)