web-dev-qa-db-fra.com

Différence entre méthode et fonction dans Scala

Je lis Fonctions Scala (une partie de Une autre visite de Scala ). Dans ce message, il a déclaré:

Méthodes et fonctions ne sont pas la même chose

Mais il n'a rien expliqué à ce sujet. Qu'est-ce qu'il essayait de dire?

242
Anantha Kumaran

Jim en a parlé à peu près dans son blog , mais je poste ici un briefing pour référence.

Voyons d’abord ce que Scala Spécification nous dit. Le chapitre 3 (types) nous parle de Types de fonctions (3.2.9) et Types de méthodes] (3.3.1). Le chapitre 4 (Déclarations de base) parle de Déclaration de valeur et définitions (4.1), Déclaration de variable et définitions (4.2) et Déclarations et définitions des fonctions (4.6). Le chapitre 6 (expressions) parle de Fonctions anonymes (6.23) et Valeurs de la méthode (6.7). Curieusement, les valeurs des fonctions sont parlées. d'une fois sur 3.2.9, et nulle part ailleurs.

A Type de fonction est (approximativement) un type de la forme (T1, ..., Tn) =>, qui est un raccourci pour le trait FunctionN dans la bibliothèque standard. Fonctions anonymes et Les valeurs de méthode ont des types de fonction, et les types de fonction peuvent être utilisé dans les déclarations et définitions de valeur, variable et fonction. En fait, cela peut faire partie d'un type de méthode.

A Le type de méthode est un type sans valeur. Cela signifie qu'il y a no valeur - aucun objet, aucune instance - avec un type de méthode. Comme mentionné ci-dessus, une valeur de méthode a en réalité un type de fonction . Un type de méthode est une déclaration def - tout ce qui concerne un def à l'exception de son corps.

Déclarations de valeurs et définitions et Les déclarations et définitions de variables sont val et var déclarations, y compris les deux type et valeur - qui peuvent être, respectivement, Type de fonction et Fonctions anonymes ou valeurs de méthodes . Notez que, sur la machine virtuelle Java, celles-ci (valeurs de méthode) sont implémentées avec ce que Java appelle "méthodes".

Une déclaration de fonction est une déclaration def, y compris type et corps. La partie type est le type de méthode et le corps est un expression ou un bloc. Ceci est également implémenté sur la machine virtuelle Java avec ce que Java appelle "méthodes".

Enfin, une fonction anonyme est une instance d'un type de fonction (c'est-à-dire , une instance du trait FunctionN), et une valeur de méthode est la même chose! La différence est qu’une valeur de méthode est créée à partir de méthodes, soit en postfixant un trait de soulignement (m _ Est une valeur de méthode correspondant à la "déclaration de fonction" (def) m) , ou par un processus appelé eta-expansion, qui est comme un transfert automatique de méthode en fonction.

C’est ce que disent les spécifications, alors laissez-moi vous dire ceci au début: nous n’utilisons pas cette terminologie! Cela conduit à trop de confusion entre so- appelé "déclaration de fonction", qui fait partie du programme (chapitre 4 - Déclarations de base) et "fonction anonyme", qui est une expression, et "type de fonction", qui est bien un type - un trait.

La terminologie ci-dessous, utilisée par des programmeurs expérimentés Scala), apporte un changement par rapport à la terminologie de la spécification: au lieu de déclaration de fonction =, nous disons méthode. Ou même déclaration de méthode. De plus, nous notons que déclarations de valeurs et déclarations de variables = sont également des méthodes à des fins pratiques.

Donc, étant donné le changement de terminologie ci-dessus, voici une explication pratique de la distinction.

Un fonction est un objet qui inclut l'un des traits FunctionX, tels que Function0, Function1, Function2, Etc. Il peut également s'agir de PartialFunction, qui étend en fait Function1.

Voyons la signature de type pour l'un de ces traits:

trait Function2[-T1, -T2, +R] extends AnyRef

Ce trait a une méthode abstraite (il a aussi quelques méthodes concrètes):

def apply(v1: T1, v2: T2): R

Et cela nous dit tout ce qu'il y a à savoir. Une fonction a une méthode apply qui reçoit N paramètres de types T1, T2, ..., TN , et retourne quelque chose de type R. Il est contre-variable sur les paramètres reçus et co-variant sur le résultat.

Cet écart signifie qu'un Function1[Seq[T], String] Est un sous-type de Function1[List[T], AnyRef]. Être un sous-type signifie qu'il peut être utilisé à la place de le. On peut facilement voir que si je vais appeler f(List(1, 2, 3)) et m'attendre à un retour de AnyRef, l'un des deux types ci-dessus fonctionnerait.

Maintenant, quelle est la similarité d'une méthode et d'une fonction? Eh bien, si f est une fonction et m est une méthode locale à la portée, les deux peuvent être appelés comme suit:

val o1 = f(List(1, 2, 3))
val o2 = m(List(1, 2, 3))

Ces appels sont en réalité différents, car le premier n'est qu'un sucre syntaxique. Scala le développe pour:

val o1 = f.apply(List(1, 2, 3))

Ce qui, bien entendu, est un appel de méthode sur l'objet f. Les fonctions ont également d'autres sucres syntaxiques à leur avantage: les littéraux de fonction (deux d'entre eux, en fait) et les signatures de type (T1, T2) => R. Par exemple:

val f = (l: List[Int]) => l mkString ""
val g: (AnyVal) => String = {
  case i: Int => "Int"
  case d: Double => "Double"
  case o => "Other"
}

Une autre similitude entre une méthode et une fonction est que la première peut être facilement convertie en seconde:

val f = m _

Scala développera that, en supposant que m type est (List[Int])AnyRef Dans (Scala 2.7):

val f = new AnyRef with Function1[List[Int], AnyRef] {
  def apply(x$1: List[Int]) = this.m(x$1)
}

Sur Scala 2.8, il utilise une classe AbstractFunction1 Pour réduire la taille des classes.

Notez que l'on ne peut pas convertir l'inverse - d'une fonction à une méthode.

Les méthodes, cependant, ont un gros avantage (enfin, deux - elles peuvent être légèrement plus rapides): elles peuvent recevoir type parameters. Par exemple, alors que f ci-dessus peut nécessairement spécifier le type de List qu'il reçoit (List[Int] Dans l'exemple), m peut le paramétrer:

def m[T](l: List[T]): String = l mkString ""

Je pense que cela couvre à peu près tout, mais je me ferai un plaisir de le compléter avec des réponses à toutes les questions qui pourraient rester.

227
Daniel C. Sobral

Une grande différence pratique entre une méthode et une fonction est ce que return signifie. return ne retourne jamais qu'une méthode. Par exemple:

scala> val f = () => { return "test" }
<console>:4: error: return outside method definition
       val f = () => { return "test" }
                       ^

Le retour d'une fonction définie dans une méthode effectue un retour non local:

scala> def f: String = {                 
     |    val g = () => { return "test" }
     | g()                               
     | "not this"
     | }
f: String

scala> f
res4: String = test

Alors que revenir d'une méthode locale ne retourne que de cette méthode.

scala> def f2: String = {         
     | def g(): String = { return "test" }
     | g()
     | "is this"
     | }
f2: String

scala> f2
res5: String = is this
65
Ben Lings

function Une fonction peut être appelée avec une liste d'arguments pour produire un résultat. Une fonction a une liste de paramètres, un corps et un type de résultat. Les fonctions membres d'une classe, d'un trait ou d'un objet singleton sont appelées méthodes . Les fonctions définies dans d'autres fonctions sont appelées fonctions locales. Les fonctions associées au type de résultat de l'unité sont appelées procédures. Les fonctions anonymes dans le code source sont appelées littéraux de fonction. Au moment de l'exécution, les littéraux de fonction sont instanciés en objets appelés valeurs de fonction.

Programmer en Scala Deuxième édition. Martin Odersky - Lex Spoon - Bill Venners

35
jamlhet

Disons que vous avez une liste

scala> val x =List.range(10,20)
x: List[Int] = List(10, 11, 12, 13, 14, 15, 16, 17, 18, 19)

Définir une méthode

scala> def m1(i:Int)=i+2
m1: (i: Int)Int

Définir une fonction

scala> (i:Int)=>i+2
res0: Int => Int = <function1>

scala> x.map((x)=>x+2)
res2: List[Int] = List(12, 13, 14, 15, 16, 17, 18, 19, 20, 21)

Méthode acceptant l'argument

scala> m1(2)
res3: Int = 4

Définir une fonction avec val

scala> val p =(i:Int)=>i+2
p: Int => Int = <function1>

L'argument pour fonctionner est facultatif

 scala> p(2)
    res4: Int = 4

scala> p
res5: Int => Int = <function1>

L'argument de la méthode est obligatoire

scala> m1
<console>:9: error: missing arguments for method m1;
follow this method with `_' if you want to treat it as a partially applied function

Vérifiez ce qui suit Tutorial qui explique comment passer d’autres différences avec des exemples tels que les autres exemples de diff avec fonction Méthode Vs, Utilisation de la fonction en tant que variables, création de la fonction qui a retourné la fonction.

29
anish

Les fonctions ne prennent pas en charge les paramètres par défaut. Les méthodes font. La conversion d'une méthode en une fonction perd les paramètres par défaut. (Scala 2.8.1)

12
eptx

Il y a un article de Nice ici duquel sont extraites la plupart de mes descriptions. Juste une courte comparaison des fonctions et méthodes concernant ma compréhension. J'espère que ça aide:

Fonctions : Ce sont essentiellement des objets. Plus précisément, les fonctions sont des objets avec une méthode d’application; Par conséquent, ils sont un peu plus lents que les méthodes en raison de leur temps système. Elle est similaire aux méthodes statiques en ce sens qu'elles sont indépendantes d'un objet à invoquer. Un exemple simple de fonction est comme ci-dessous:

val f1 = (x: Int) => x + x
f1(2)  // 4

La ligne ci-dessus n'est rien d'autre que l'attribution d'un objet à un autre comme objet1 = objet2. En réalité, l'objet2 dans notre exemple est une fonction anonyme et le côté gauche obtient le type d'un objet à cause de cela. Par conséquent, maintenant f1 est un objet (Fonction). La fonction anonyme est en fait une instance de Function1 [Int, Int], ce qui signifie une fonction avec 1 paramètre de type Int et une valeur renvoyée de type Int. Appeler f1 sans les arguments nous donnera la signature de la fonction anonyme (Int => Int =)

Méthodes : Ce ne sont pas des objets, mais sont affectés à une instance d'une classe, c'est-à-dire un objet. Identique à la méthode de Java ou aux fonctions membres en c ++ (comme Raffi Khatchadourian indiqué dans un commentaire à cette question ), etc. Voici un exemple simple de méthode:

def m1(x: Int) = x + x
m1(2)  // 4

La ligne ci-dessus n'est pas une simple attribution de valeur, mais une définition d'une méthode. Lorsque vous appelez cette méthode avec la valeur 2 comme la deuxième ligne, le x est remplacé par 2 et le résultat est calculé et vous obtenez 4 en sortie. Ici, vous obtiendrez une erreur si vous écrivez simplement m1 car c'est une méthode et vous avez besoin de la valeur d'entrée. En utilisant _, vous pouvez affecter une méthode à une fonction comme celle-ci:

val f2 = m1 _  // Int => Int = <function1>
6
Mehran

Voici un bon post de Rob Norris qui explique la différence, voici un TL; DR

Les méthodes dans Scala ne sont pas des valeurs, mais des fonctions. Vous pouvez construire une fonction qui délègue une méthode à l'aide d'une méthode η-expansion (déclenchée par le tiret bas de la fin).

avec la définition suivante:

a method is something defined with def and a value is something you can assign to a val

En un mot ( extrait du blog):

Lorsque nous définissons une méthode, nous voyons que nous ne pouvons pas l'affecter à un val.

scala> def add1(n: Int): Int = n + 1
add1: (n: Int)Int

scala> val f = add1
<console>:8: error: missing arguments for method add1;
follow this method with `_' if you want to treat it as a partially applied function
       val f = add1

Notez également le type de add1, qui n’a pas l’air normal; vous ne pouvez pas déclarer une variable de type (n: Int)Int. Les méthodes ne sont pas des valeurs.

Cependant, en ajoutant l'opérateur postfixant η-expansion (η est prononcé "eta"), nous pouvons transformer la méthode en une valeur de fonction. Notez le type de f.

scala> val f = add1 _
f: Int => Int = <function1>

scala> f(3)
res0: Int = 4

L'effet de _ consiste à effectuer l'équivalent de ce qui suit: nous construisons un Function1 _ instance qui délègue à notre méthode.

scala> val g = new Function1[Int, Int] { def apply(n: Int): Int = add1(n) }
g: Int => Int = <function1>

scala> g(3)
res18: Int = 4
2
Valy Dia