直角三角形的斜边的平方等于另外两边的平方和。
这是毕达哥拉斯定理。根据其边的长度“a”和“b”计算斜边的函数将返回sqrt(a * a + b * b)。
问题是,如何在Scala中定义这样一个函数,以便它可以用于实现适当方法的任何类型?
对于上下文,想象一下你想要使用Int,Double,Int-Rational,Double-Rational,BigInt或BigInt-Rational类型的整个数学定理库,具体取决于你在做什么,速度,精度,准确性和范围要求。
答案 0 :(得分:24)
这仅适用于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
。
答案 1 :(得分:18)
最明显的方式:
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
,并且定义+
,*
并且存在{{1},则无法解决结构类型问题功能。
最后,我们来到最基本的问题:它过于复杂。为什么要以这种方式实现功能呢?实际上,它需要应用的唯一类型是真正的Scala数字。因此,最简单的做法就是:
sqrt
所有问题都解决了!由于隐式转换的奇迹,此函数可用于def pyth(a: Double, b: Double) = Math.sqrt(a * a + b * b)
,Double
,Int
类型的值,甚至是Float
等奇数值。虽然这个功能在技术上不如我们的结构类型版本灵活,但它非常更高效,并且显着更易读。对于定义Short
和+
的无法预料的类型,我们可能已经失去了计算Pythagrean定理的能力,但我认为你不会错过这种能力。
答案 2 :(得分:2)
关于丹尼尔回答的一些想法:
我experimented将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,您可以几乎。我得到了frustratringly close!
答案 3 :(得分:0)
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);