The square of the hypotenuse of a right triangle is equal to the sum of the squares on the other two sides.
This is Pythagoras's Theorem. A function to calculate the hypotenuse based on the length "a" and "b" of it's sides would return sqrt(a * a + b * b).
The question is, how would you define such a function in Scala in such a way that it could be used with any type implementing the appropriate methods?
For context, imagine a whole library of math theorems you want to use with Int, Double, Int-Rational, Double-Rational, BigInt or BigInt-Rational types depending on what you are doing, and the speed, precision, accuracy and range requirements.
The formula of Pythagoras theorem is expressed as, Hypotenuse2 = Base2 + Height2. This is also written as, c2 = a2 + b2; where 'c' is the hypotenuse and 'a' and 'b' are the two legs of the right-angled triangle.
Solving for the hypotenuse, we simply take the square root of both sides of the equation a² + b² = c² and solve for c . When doing so, we get c = √(a² + b²) . This is just an extension of the Pythagorean theorem and often is not associated with the name hypotenuse formula.
: a theorem in geometry: the square of the length of the hypotenuse of a right triangle equals the sum of the squares of the lengths of the other two sides.
The Pythagorean Theorem was one of the earliest theorems known to ancient civilizations. This famous theorem is named for the Greek mathematician and philosopher, Pythagoras. Pythagoras founded the Pythagorean School of Mathematics in Cortona, a Greek seaport in Southern Italy.
This only works on Scala 2.8, but it does work:
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
More generally speaking, the trait Numeric
is effectively a reference on how to solve this type of problem. See also Ordering
.
The most obvious way:
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)
This is horrible for many reasons. First, we have the problem of the recursive type, Num
. This is only allowed if you compile this code with the -Xrecursive
option set to some integer value (5 is probably more than sufficient for numbers). Second, the type Num
is structural, which means that any usage of the members it defines will be compiled into corresponding reflective invocations. Putting it mildly, this version of pyth
is obscenely inefficient, running on the order of several hundred thousand times slower than a conventional implementation. There's no way around the structural type though if you want to define pyth
for any type which defines +
, *
and for which there exists a sqrt
function.
Finally, we come to the most fundamental issue: it's over-complicated. Why bother implementing the function in this way? Practically speaking, the only types it will ever need to apply to are real Scala numbers. Thus, it's easiest just to do the following:
def pyth(a: Double, b: Double) = Math.sqrt(a * a + b * b)
All problems solved! This function is usable on values of type Double
, Int
, Float
, even odd ones like Short
thanks to the marvels of implicit conversion. While it is true that this function is technically less flexible than our structurally-typed version, it is vastly more efficient and eminently more readable. We may have lost the ability to calculate the Pythagrean theorem for unforeseen types defining +
and *
, but I don't think you're going to miss that ability.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With