24

直角三角形斜边的平方等于其他两条边的平方和。

这就是毕达哥拉斯定理。根据边的长度“a”和“b”计算斜边的函数将返回 sqrt(a * a + b * b)。

问题是,您将如何在 Scala 中定义这样的函数,使其可以与任何实现适当方法的类型一起使用?

对于上下文,想象一下您想要与 Int、Double、Int-Rational、Double-Rational、BigInt 或 BigInt-Rational 类型一起使用的整个数学定理库,具体取决于您在做什么,以及速度、精度、准确性和范围要求.

4

4 回答 4

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

于 2009-09-06T05:32:22.943 回答
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但是,如果您想为任何定义并且存在函数的类型定义+,则无法绕过结构类型。*sqrt

最后,我们来到了最基本的问题:它过于复杂。为什么要以这种方式实现该功能?实际上,它需要应用的唯一类型是真实的 Scala 数字。因此,最简单的方法是执行以下操作:

def pyth(a: Double, b: Double) = Math.sqrt(a * a + b * b)

所有问题都解决了!由于隐式转换的奇迹,此函数可用于类型Double, Int,Float甚至奇数的值。Short虽然这个函数在技术上确实不如我们的结构类型版本灵活,但它的效率要高得多,而且可读性要强得多。+我们可能已经失去了为定义和的不可预见类型计算勾股定理的能力*,但我认为你不会错过这种能力。

于 2009-01-28T05:29:38.003 回答
2

关于丹尼尔的回答的一些想法:

我已经尝试泛化NumericReal,这会更适合这个函数来提供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如果在第二个参数列表中传递 ,则类型推断效果更好。

参数ab将作为对象传递,但@specialized 可以解决这个问题。不幸的是,数学运算仍然会有一些开销。

几乎可以不导入 mkNumericOps。我沮丧地接近了!

于 2010-03-05T16:48:17.587 回答
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);
于 2010-03-05T14:25:43.037 回答