1

我为了科学编程而学习scala。我正在尝试使用 spire 编写一些简单的通用代码。我有以下示例工作:

import spire.algebra._
import spire.math._
import spire.implicits._
object TestSqrt {
    def testSqrt[T: Numeric](x: T) = {
        sqrt(x)
    }

    def main(args: Array[String]){
        val x_d: Double = 4.0
        println(testSqrt(x_d))

        val x_i: Int = 4
        println(testSqrt(x_i))
    }
}

正如预期的那样,这将打印出 2.0 和 2。问题是我不能用 exp 函数得到同样的东西。以下代码无法编译:

import spire.algebra._
import spire.math._
import spire.implicits._
object TestExp {
    def testExp[T: Numeric](x: T) = {
        exp(x)
    }

    def main(args: Array[String]){
        val x_d: Double = 4.0
        println(testExp(x_d))

        val x_i: Int = 4
        println(testExp(x_i))
    }
}

编译器说:

... could not find implicit value for parameter t: spire.algebra.Trig[T]
[error]         exp(x)
[error]            ^  
[error] one error found
[error] (compile:compile) Compilation failed

我做错了什么,还不支持 exp,还是这是一个错误?任何人都可以提供一个使用 exp 和来自 spire 的通用数字类型的工作示例吗?

更新:

我可以通过使用 Trig 而不是 Numeric 来使 exp 工作,如下所示:

import spire.algebra._
import spire.math._
import spire.implicits._
object TestExp {
    def testExp[T: Trig](x: T) = {
        exp(x)
    }

    def main(args: Array[String]){
        val x_d: Double = 1.0
        println(testExp(x_d))

        val x_f: Float = 1.0f
        println(testExp(x_f))
        // val x_i: Int = 4
        // println(testExp(x_i))
    }
}

但是,它不适用于 Int,仅适用于浮点类型。此外,如果我使用 Trig,则无法使用 sqrt。以下代码无法编译:

}

import spire.algebra._
import spire.math._
import spire.implicits._
object TestSqrt {
    def testSqrt[T: Trig](x: T) = {
        sqrt(x)
    }

    def main(args: Array[String]){
        val x_d: Double = 4.0
        println(testSqrt(x_d))

        val x_i: Int = 4
        println(testSqrt(x_i))
    }
}

并给出错误:

... could not find implicit value for evidence parameter of type spire.algebra.Trig[Int]
[error]         println(testSqrt(x_i))
[error]                         ^
[error] two errors found
[error] (compile:compile) Compilation failed

如果我想要 exp 和 sqrt,我应该怎么做,有没有办法让 exp 与 Integral 类型一起工作?

4

1 回答 1

3

类型类被定义为具有相同类型的操作数和操作结果。也就是说,当你sqrt对 type 的值运行时A,结果也将是 type A

现在您可能会争辩说sqrt,整数会为某些特定输入值产生整数,例如您在示例中使用的 4。另一方面,应该sqrt(5)返回什么?似乎它将返回小于实际平方根的最大整数:

def testSqrt[T: Numeric](x: T): T = sqrt(x)

scala> testSqrt(5)
res0: Int = 2

这几乎不是你想要的。有趣的是,当您sqrt直接调用该函数时,它会返回一个Double

scala> sqrt(5)
res1: Double = 2.23606797749979

原因是当你导入时spire.math._你会得到一个重载的sqrt函数:

final def sqrt(x: Double): Double = Math.sqrt(x)
final def sqrt[A](a: A)(implicit ev: NRoot[A]): A = ev.sqrt(a)

这通过赋予第一个变体优先级来“解决”问题(因为它不需要隐式参数)。在您的示例中发生的是您使用了通用签名,因此sqrt将调用第二个版本。该NRoot参数是间接提供的,因为您说您有一个Numeric.

所以你必须决定你要做什么——你是想Double在计算平方根时回退,还是采用这种可能截断结果的非常规想法?


现在来求幂。在math中,还有 的重载版本exp,其中:

final def exp(n: Double): Double = Math.exp(n)
final def exp[A](a: A)(implicit t: Trig[A]): A = t.exp(a)

不同的是,在任何情况下sqrt你都不会得到。Trig[Int]只是没有有意义的方法。

让我们定义一个可以基于类型类调用sqrt的函数:exp

def sqrtAndExp[T: NRoot: Trig](x: T): (T, T) = (sqrt(x), exp(x))

您会看到您可以在函数声明中使用多个上下文绑定。此语法等价于

def sqrtAndExp[T](x: T)(implicit nroot: NRoot[T], trig: Trig[T]: (T, T) =
  (sqrt(x), exp(x))

对于双打,此功能有效(假设您已导入spire.implicits._):

scala> sqrtAndExp(5.0)
res2: (Double, Double) = (2.23606797749979,148.4131591025766)

但不适用于整数:

scala> sqrtAndExp(5)
<console>:18: error: could not find implicit value for evidence parameter of type
                     spire.algebra.Trig[Int]
              sqrtAndExp(5)
                        ^

但是,在需要双精度的地方总是可以使用整数。所以你可以让它工作:

scala> sqrtAndExp[Double](5)
res3: (Double, Double) = (2.23606797749979,148.4131591025766)
于 2013-12-11T13:31:38.210 回答