scala - maquetas - teorema de pitagoras historia
¿Cómo se escribe el Teorema de Pitágoras en Scala? (4)
El cuadrado de la hipotenusa de un triángulo rectángulo es igual a la suma de los cuadrados en los otros dos lados.
Este es el Teorema de Pitágoras. Una función para calcular la hipotenusa basada en la longitud "a" y "b" de sus lados devolvería sqrt (a * a + b * b).
La pregunta es, ¿cómo definirías esa función en Scala de tal manera que se pueda usar con cualquier tipo que implemente los métodos apropiados?
Para el contexto, imagina toda una biblioteca de teoremas matemáticos que quieras usar con los tipos Int, Double, Int-Rational, Double-Rational, BigInt o BigInt-Rational según lo que estés haciendo, y los requisitos de velocidad, precisión, precisión y rango. .
Algunos pensamientos sobre la respuesta de Daniel:
He experimented para generalizar Numeric
a Real
, que sería más apropiado para esta función para proporcionar la función sqrt
. Esto daría como resultado:
def pythagoras[T](a: T, b: T)(implicit n: Real[T]) = {
import n.mkNumericOps
(a*a + b*b).sqrt
}
Es complicado, pero posible, utilizar números literales en tales funciones genéricas.
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
}
La inferencia de tipo funciona mejor si el sqrt
se pasa en una segunda lista de parámetros.
Los parámetros a
y b
se pasarán como Objetos, pero @specialized podría solucionar esto. Desafortunadamente todavía habrá algunos gastos generales en las operaciones de matemáticas.
Casi puede prescindir de la importación de mkNumericOps. ¡Me siento frustrantemente cerca!
Esto solo funciona en Scala 2.8, pero funciona:
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
En términos más generales, el rasgo Numeric
es efectivamente una referencia sobre cómo resolver este tipo de problema. Ver también Ordering
.
Hay un método en java.lang.Math:
public static double hypot (double x, double y)
por lo que los javadocs afirman:
Devuelve sqrt (x2 + y2) sin desbordamiento intermedio o subdesbordamiento.
investigando src.zip, Math.hypot usa StrictMath, que es un método nativo:
public static native double hypot(double x, double y);
La forma más obvia:
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)
Esto es horrible por muchas razones. Primero, tenemos el problema del tipo recursivo, Num
. Esto solo está permitido si compila este código con la opción -Xrecursive
establecida en un valor entero (5 es probablemente más que suficiente para los números). En segundo lugar, el tipo Num
es estructural, lo que significa que cualquier uso de los miembros que define se compilará en las correspondientes invocaciones reflexivas. Para pyth
suavemente, esta versión de pyth
es obscenamente ineficiente, corriendo en el orden de varios cientos de miles de veces más lento que una implementación convencional. Sin embargo, no hay forma de evitar el tipo estructural si se quiere definir pyth
para cualquier tipo que defina +
, *
y para el cual exista una función sqrt
.
Finalmente, llegamos al tema más fundamental: es demasiado complicado. ¿Por qué molestarse en implementar la función de esta manera? Hablando en términos prácticos, los únicos tipos a los que tendrá que aplicar son los números reales de Scala. Por lo tanto, es más fácil hacer lo siguiente:
def pyth(a: Double, b: Double) = Math.sqrt(a * a + b * b)
Todos los problemas resueltos! Esta función se puede utilizar en valores de tipo Double
, Int
, Float
, incluso impares, como Short
gracias a las maravillas de la conversión implícita. Si bien es cierto que esta función es técnicamente menos flexible que nuestra versión estructuralmente tipada, es mucho más eficiente y eminentemente más legible. Es posible que hayamos perdido la capacidad de calcular el teorema de Pythagrean para tipos imprevistos que definen +
y *
, pero no creo que vayas a perder esa habilidad.