如何从隐式提供的数字'convert back' Scala 到原始数字类型?

  • 本文关键字:数字 Scala back 原始 类型 convert scala
  • 更新时间 :
  • 英文 :


我正在玩一些隐式(我目前正在学习Scala),并编写了一个简单的类来表示温度,并进行了比例转换。它使用'TempOps'类来提供对数字的隐式操作,因此我可以编写如下内容:

(5 Celsius) asFahrenheight

因为我希望它适用于任何数字类型(至少是内置类型),所以我使用隐式numeric类型类抽象TempOps类。代码如下:

package exercises.temperature
/**
 * Created by steve on 9/16/2015.
 */
sealed case class Temperature(kelvin: Double) {
  import Temperature._
  override def toString():String = s"$kelvin Kelvin"
  def asKelvin = kelvin
  def asFahrenheight = ctof(ktoc(kelvin))
  def asCelsius = ktoc(kelvin)
  def asK = asKelvin
  def asF = asFahrenheight
  def asC = asCelsius
}
sealed case class TempOps[N](t: N)(implicit n: Numeric[N]) {
  implicit val Fahrenheit = Temperature.Fahrenheit(n.toDouble(t))
  implicit val Celsius = Temperature.Celsius(n.toDouble(t))
  implicit val Kelvin = Temperature(n.toDouble(t))
}
object Temperature {
  private val absoluteZeroC = -273.15
  private def ftoc(f: Double) = (f-32)*5/9
  private def ctok(c: Double) = c - absoluteZeroC
  private[temperature] def ktoc(k: Double) = k + absoluteZeroC
  private[temperature] def ctof(c: Double) = c*9/5 + 32
  private[temperature] def Fahrenheit(f: Double) = Temperature(ctok(ftoc(f)))
  private[temperature] def Celsius(c: Double) = Temperature(ctok(c))
  implicit def toTempOps(n: Int) = TempOps(n)
}

这可以工作,但是假设我想保留原始的数字类型,以便在下面的结果中仍然是Int而不是Double(显然可以舍入一些):

val originalTempValue: Int = 5  // Explicitly an Int
val converted = (originalTempValue Celsius) asFahrenheit

'已转换'现在将是Double类型。我如何修改tempop以"保留"隐式使用的数字类型,以便在上面的"转换"将最终成为Int?

您需要从值的来源跟踪原始T,然后定义从double到该T的转换

例如:

trait ConvertTemp[T] extends (Double ⇒ T)
object ConvertTemp {
    def apply[T](f: Double ⇒ T) = new ConvertTemp[T] { override def apply(v: Double) = f(v) }
    implicit val convertToInt = apply(Math round _)
    implicit val convertToDouble = apply(identity)
}
sealed case class Temperature[T](kelvin: Double)(implicit convert: ConvertTemp[T]) {
  import Temperature._
  override def toString(): String = s"$kelvin Kelvin"
  def asKelvin = convert(kelvin)
  def asFahrenheight = ctof(ktoc(kelvin))
  def asCelsius = ktoc(kelvin)
  def asK = asKelvin
  def asF = asFahrenheight
  def asC = asCelsius
}
object Temperature {
  private val absoluteZeroC = -273.15
  private def ftoc(f: Double) = (f - 32) * 5 / 9
  private def ctok(c: Double) = c - absoluteZeroC
  private[temperature] def ktoc(k: Double) = k + absoluteZeroC
  private[temperature] def ctof(c: Double) = c * 9 / 5 + 32
  private[temperature] def Fahrenheit[T](f: Double)(implicit convert: ConvertTemp[T]) = Temperature(ctok(ftoc(f)))
  private[temperature] def Celsius[T](c: Double)(implicit convert: ConvertTemp[T]) = Temperature(ctok(c))
  implicit def toTempOps(n: Int) = TempOps(n)
}
sealed case class TempOps[N](t: N)(implicit n: Numeric[N]) {
  implicit val Fahrenheit = Temperature.Fahrenheit(n.toDouble(t))
  implicit val Celsius = Temperature.Celsius(n.toDouble(t))
  implicit val Kelvin = Temperature(n.toDouble(t))
}

根据上面emilianogc的建议,我得出了下面的解决方案。

package exercises.temperature
/**
 * Created by steve on 9/16/2015.
 */
trait ConvertTemp[T] extends (Double => T)
object ConvertTemp {
  def apply[T](f: Double => T) = new ConvertTemp[T] { override def apply(v: Double) = f(v) }
  implicit val convertToInt = apply(x=>Math.round(x).toInt)
  implicit val convertToLong = apply(Math.round _)
  implicit val convertToDouble = apply(identity)
}
sealed case class Temperature[N](private val kelvin: Double)(convert: ConvertTemp[N]) {
  import Temperature._
  override def toString():String = s"$kelvin Kelvin"
  def asKelvin = convert(kelvin)
  def asFahrenheight = convert(ctof(ktoc(kelvin)))
  def asCelsius = convert(ktoc(kelvin))
  def asK = asKelvin
  def asF = asFahrenheight
  def asC = asCelsius
}
sealed case class TempOps[N](t: N)(implicit n: Numeric[N], convert: ConvertTemp[N]) {
  implicit val Fahrenheit = Temperature.Fahrenheit(t)(n,convert)
  implicit val Celsius = Temperature.Celsius(t)(n,convert)
  implicit val Kelvin = Temperature.Kelvin(t)(n,convert)
}
object Temperature {
  private val absoluteZeroC = -273.15
  private def ftoc(f: Double) = (f-32)*5/9
  private def ctok(c: Double) = c - absoluteZeroC
  private[temperature] def ktoc(k: Double) = k + absoluteZeroC
  private[temperature] def ctof(c: Double) = c*9/5 + 32
  private[temperature] def Fahrenheit[N](f: N)(n: Numeric[N], convert: ConvertTemp[N]) = Temperature(ctok(ftoc(n.toDouble(f))))(convert)
  private[temperature] def Celsius[N](c: N)(n: Numeric[N], convert: ConvertTemp[N]) = Temperature(ctok(n.toDouble(c)))(convert)
  private[temperature] def Kelvin[N](c: N)(n: Numeric[N], convert: ConvertTemp[N]) = Temperature(n.toDouble(c))(convert)
  implicit def toTempOps(t: Int) = TempOps(t)
  implicit def toTempOps(t: Long) = TempOps(t)
  implicit def toTempOps(t: Double) = TempOps(t)
}

我仍然不满意的是,在传递数字类类型和转换器时,在它不能隐式的地方似乎有相当多的"样板语言"(因为我们已经在处理只能通过其他隐式推断的泛型类型,我认为)。如果有人能够找到一种方法,使它在这方面不那么混乱,并且仍然编译(我最初期望的一些显式类型类可以推断,但如果我试图在上面的隐式代码中做更多的事情,编译器似乎不想玩球)

最新更新