直角三角形斜边的平方等于其他两条边的平方和。
这就是毕达哥拉斯定理。根据边的长度“a”和“b”计算斜边的函数将返回 sqrt(a * a + b * b)。
问题是,您将如何在 Scala 中定义这样的函数,使其可以与任何实现适当方法的类型一起使用?
对于上下文,想象一下您想要与 Int、Double、Int-Rational、Double-Rational、BigInt 或 BigInt-Rational 类型一起使用的整个数学定理库,具体取决于您在做什么,以及速度、精度、准确性和范围要求.
直角三角形斜边的平方等于其他两条边的平方和。
这就是毕达哥拉斯定理。根据边的长度“a”和“b”计算斜边的函数将返回 sqrt(a * a + b * b)。
问题是,您将如何在 Scala 中定义这样的函数,使其可以与任何实现适当方法的类型一起使用?
对于上下文,想象一下您想要与 Int、Double、Int-Rational、Double-Rational、BigInt 或 BigInt-Rational 类型一起使用的整个数学定理库,具体取决于您在做什么,以及速度、精度、准确性和范围要求.
这仅适用于 Scala 2.8,但确实有效:
scala> def pythagoras[T](a: T, b: T, sqrt: T => T)(implicit n: Numeric[T]) = {
| import n.mkNumericOps
| sqrt(a*a + b*b)
| }
pythagoras: [T](a: T,b: T,sqrt: (T) => T)(implicit n: Numeric[T])T
scala> def intSqrt(n: Int) = Math.sqrt(n).toInt
intSqrt: (n: Int)Int
scala> pythagoras(3,4, intSqrt)
res0: Int = 5
更一般地说,特征Numeric是如何解决此类问题的有效参考。另请参阅Ordering。
最明显的方式:
type Num = {
def +(a: Num): Num
def *(a: Num): Num
}
def pyth[A <: Num](a: A, b: A)(sqrt: A=>A) = sqrt(a * a + b * b)
// usage
pyth(3, 4)(Math.sqrt)
这很可怕,原因有很多。首先,我们遇到了递归类型的问题Num。仅当您使用设置为某个整数值的选项编译此代码时才允许这样做-Xrecursive(对于数字来说,5 可能绰绰有余)。其次,类型Num是结构化的,这意味着它定义的成员的任何使用都将编译为相应的反射调用。说得委婉一点,这个版本的pyth效率非常低,运行速度比传统实现慢几十万倍。pyth但是,如果您想为任何定义并且存在函数的类型定义+,则无法绕过结构类型。*sqrt
最后,我们来到了最基本的问题:它过于复杂。为什么要以这种方式实现该功能?实际上,它需要应用的唯一类型是真实的 Scala 数字。因此,最简单的方法是执行以下操作:
def pyth(a: Double, b: Double) = Math.sqrt(a * a + b * b)
所有问题都解决了!由于隐式转换的奇迹,此函数可用于类型Double, Int,Float甚至奇数的值。Short虽然这个函数在技术上确实不如我们的结构类型版本灵活,但它的效率要高得多,而且可读性要强得多。+我们可能已经失去了为定义和的不可预见类型计算勾股定理的能力*,但我认为你不会错过这种能力。
关于丹尼尔的回答的一些想法:
我已经尝试泛化Numeric到Real,这会更适合这个函数来提供sqrt函数。这将导致:
def pythagoras[T](a: T, b: T)(implicit n: Real[T]) = {
import n.mkNumericOps
(a*a + b*b).sqrt
}
在这样的通用函数中使用文字数字很棘手,但可能。
def pythagoras[T](a: T, b: T)(sqrt: (T => T))(implicit n: Numeric[T]) = {
import n.mkNumericOps
implicit val fromInt = n.fromInt _
//1 * sqrt(a*a + b*b) Not Possible!
sqrt(a*a + b*b) * 1 // Possible
}
sqrt如果在第二个参数列表中传递 ,则类型推断效果更好。
参数a和b将作为对象传递,但@specialized 可以解决这个问题。不幸的是,数学运算仍然会有一些开销。
您几乎可以不导入 mkNumericOps。我沮丧地接近了!
java.lang.Math中有一个方法:
public static double hypot (double x, double y)
javadocs为此断言:
返回 sqrt(x2 +y2) 没有中间溢出或下溢。
查看 src.zip,Math.hypot 使用 StrictMath,这是一种原生方法:
public static native double hypot(double x, double y);