如何在Scala中写出毕达哥拉斯定理?

时间:2009-01-27 23:57:24

标签: scala

  

直角三角形的斜边的平方等于另外两边的平方和。

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

问题是,如何在Scala中定义这样一个函数,以便它可以用于实现适当方法的任何类型?

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

4 个答案:

答案 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) DoubleInt类型的值,甚至是Float等奇数值。虽然这个功能在技术上不如我们的结构类型版本灵活,但它非常更高效,并且显着更易读。对于定义Short+的无法预料的类型,我们可能已经失去了计算Pythagrean定理的能力,但我认为你不会错过这种能力。

答案 2 :(得分:2)

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

experimentedNumeric概括为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,则类型推断效果会更好。

参数ab将作为对象传递,但@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);