2010-10-29 9 views
29

Je commence juste avec Scala et quelque chose que je pense devrait être facile est difficile à comprendre. Je suis en train de mettre en œuvre la fonction suivante:Comment puis-je implémenter une fonction mathématique générique dans Scala

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

Cela fonctionne très bien, mais si je veux essayer de faire fonctionner cette fonction pour tout type de numéro que je voudrais être en mesure de faire ce qui suit:

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

cela se plaint et dit: erreur: valeur * n'est pas membre du paramètre type T

Ai-je besoin de mettre en œuvre un trait pour cela?

+0

Pour la postérité, il pourrait être utile de donner à cette question un titre plus spécifique comme "Comment implémenter une fonction mathématique générique dans Scala?" –

+0

D'accord, merci. – jimmyb

Répondre

30

C'était l'un de mes first questions dans Stack Overflow ou à propos de Scala. Le problème est que Scala maintient la compatibilité avec Java, ce qui signifie que ses types numériques de base sont équivalents aux primitives de Java. Le problème se pose en ce que les primitives Java ne sont pas des classes, et, par conséquent, n'ont pas de hiérarchie de classe qui autoriserait un supertype "numérique".

Pour être plus clair, Java, et, par conséquent, Scala, ne voit pas de motifs communs entre un Double « s + et un Int » s +.

La façon dont Scala a finalement obtenu autour de cette restriction était en utilisant Numeric et ses sous-classes Fractional et Integral, dans la soi-disant modèle de classe de types. En gros, vous l'utilisez comme ceci:

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

Ou, si vous n'avez pas besoin des opérations numériques, mais les méthodes que vous appelez le faites, vous pouvez utiliser la syntaxe liée contexte pour la déclaration de type:

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

Pour plus d'informations, voir les réponses dans ma propre question.

+1

Belle explication. Je devais google pour le modèle Typeclass - ne savais pas qu'il existait. Merci. – Collin

+0

Et si vous avez ceci: 'def squareAddOne [T] (x: T) (nombre implicite: Numérique [T]): T = {numéro d'importation; x * x + 1} ' Comment souhaitez-vous ajouter un élément à un tableau numérique? –

+0

Oh, je pense que ma question est répondue ici: http://stackoverflow.com/a/2388386/901263 –

10

Vous pouvez définir square comme:

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

Cette approche a l'avantage que cela fonctionne pour tout type T qui a une conversion implicite numérique [T] (c.-à-Int, Float, Double, Char, BigInt, ..., ou tout type pour lequel vous fournissez une conversion implicite).

Edit: Malheureusement, vous avez des problèmes si vous essayez quelque chose comme List(1,2,3).map(square) (en particulier, vous obtiendrez une erreur de compilation comme « n'a pas pu trouver la valeur implicite du paramètre de preuve de type numérique [T] » . pour éviter ce problème, vous pouvez surcharger square pour retourner une fonction:.

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

Espérons que quelqu'un avec une meilleure compréhension du inferencer type expliquera pourquoi c'est

Alternativement, on peut appeler List(1,2,3).map(square(_)), comme Derek Williams a souligné dans le scala-user mailing list thread.

Questions connexes