我正在玩弄一些暗示(我现在正在学习Scala)并编写了一个简单的类来表示温度,并进行缩放转换。这使用'TempOps'类来提供数字的隐式操作,因此我可以编写如下内容:
(5 Celsius) asFahrenheight
因为我希望它适用于任何数字类型(至少是内置的),我使用隐式数字类型抽象了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
'已转换'现在将成为双倍。如何修改TempOps以“保留”隐式使用的数字类型,以便在上面的“转换”中最终成为一个Int?
答案 0 :(得分:1)
您需要跟踪值来自的原始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))
}
答案 1 :(得分:0)
使用上面的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)
}
我仍然感到不满的是,似乎有一些“样板”问题。在传递数值类类型和转换器的地方,它不能隐含(因为我们已经处理过只能通过其他含义推断的泛型类型,我认为)。如果有人能够找到一种方法来使这一点在这方面稍微混乱,并仍然编译(我最初期望的一些显式类型类可以推断,但编译器似乎并不想要如果我试图在上面的代码中做更多的事情,那就打球吧。