2010-10-29 18 views
29

Estoy empezando con Scala y algo que creo que debería ser fácil es difícil de entender. Estoy tratando de poner en práctica la siguiente función:Cómo implemento una función matemática genérica en Scala

def square(x:Int):Int = { x * x }

Esto funciona muy bien, pero si quiero tratar de hacer este trabajo función para cualquier tipo de número que le gustaría ser capaz de hacer lo siguiente:

def square[T <: Number](x : T):T = { x * x }

esto se queja y dice: error: valor * no es miembro del tipo de parámetro T

¿es necesario poner en práctica un rasgo para esto?

+0

Para la posteridad, podría ser útil dar a esta pregunta un título más específico como "¿Cómo implemento una función matemática genérica en Scala?" –

+0

De acuerdo, gracias. – jimmyb

Respuesta

30

Esa fue una de mis first questions en Stack Overflow o acerca de Scala. El problema es que Scala mantiene la compatibilidad con Java, y eso significa que sus tipos numéricos básicos son equivalentes a las primitivas de Java.

El problema surge porque las primitivas de Java no son clases, y, por lo tanto, no tienen una jerarquía de clases que permita un supertipo "numérico".

Para decirlo más claramente, Java y, por lo tanto, Scala, no ve ningún motivo común entre un Double 's + y un Int' s +.

La forma Scala finalmente consiguió evitar esta restricción era mediante el uso de Numeric, y sus subclases FractionalIntegral y, en el llamado patrón clase de tipos. Básicamente, se utiliza de esta manera:

def square[T](x: T)(implicit num: Numeric[T]): T = { 
    import num._ 
    x * x 
} 

O, si usted no necesita ninguna de las operaciones numéricas, pero los métodos que llaman hacer, se puede utilizar el atado de sintaxis contexto para la declaración de tipo:

def numberAndSquare[T : Numeric](x: T) = x -> square(x) 

Para obtener más información, vea las respuestas en mi propia pregunta.

+1

Buena explicación. Tuve que buscar el patrón de clase de letra en Google, no sabía que existía. Gracias. – Collin

+0

¿Qué pasa si tiene esto sin embargo: 'def squareAddOne [T] (x: T) (num implícito: numérico [T]): T = {import num._; x * x + 1} ' ¿Cómo agregaría un Int a un Numérico? –

+0

Oh, creo que mi pregunta es respondida aquí: http://stackoverflow.com/a/2388386/901263 –

10

Puede definir square como:

def square[T: Numeric](x: T): T = implicitly[Numeric[T]].times(x,x) 

Este enfoque tiene la ventaja de que funcionará para cualquier tipo T que tiene una conversión implícita a Numeric [T] (es decir, Int, Float, Double, Char, BigInt, ..., o cualquier tipo para el que proporcione una conversión implícita).

Editar: Por desgracia, se encontrará con problemas si intenta algo así como List(1,2,3).map(square) (en concreto, obtendrá un error de compilación como "no se pudo encontrar el valor implícito para el parámetro evidencia de tipo numérico [T]" . para evitar este problema, puede sobrecargar square para devolver una función:.

object MyMath { 
    def square[T: Numeric](x: T) = implicitly[Numeric[T]].times(x,x) 
    def square[T: Numeric]: T => T = square(_) 
} 

Esperemos que alguien con una mejor comprensión de la inferencer tipo explicará por qué esto es

Alternativamente, se puede llamar al List(1,2,3).map(square(_)), como señaló Derek Williams en el scala-user mailing list thread.

Cuestiones relacionadas