84 votes

Scala nourrissage vs partiellement appliquée fonctions

Désolé pour le newbie question. Je me rends compte qu'il y a plusieurs questions ici sur ce nourrissage et appliquée partiellement les fonctions sont, mais je me demande comment ils sont différents. Comme exemple simple, voici un curry de fonction pour trouver les nombres:

def filter(xs: List[Int], p: Int => Boolean): List[Int] =
   if (xs.isEmpty) xs
   else if (p(xs.head)) xs.head :: filter(xs.tail, p)
   else filter(xs.tail, p)

def modN(n: Int)(x: Int) = ((x % n) == 0)

Donc vous pouvez écrire de la manière suivante pour utiliser ce:

val nums = List(1,2,3,4,5,6,7,8)
println(filter(nums, modN(2))

qui retourne: List(2,4,6,8). Mais j'ai trouvé que je peux faire la même chose de cette façon:

def modN(n: Int, x: Int) = ((x % n) == 0)

val p = modN(2, _: Int)
println(filter(nums, p))

ce qui renvoie également: List(2,4,6,8).

Donc ma question est, quelle est la principale différence entre les deux, et quand souhaitez-vous utiliser un sur l'autre? Est-ce juste trop simpliste d'un exemple pour montrer pourquoi l'un serait utilisé plutôt que l'autre? Merci beaucoup pour votre aide.

89voto

x3ro Points 12721

La différence sémantique a été expliqué assez bien dans la réponse liée à la Plastie Grove.

En termes de fonctionnalités, il ne semble pas beaucoup de différence, si. Regardons quelques exemples pour vérifier que. Tout d'abord, une fonction normale:

scala> def modN(n: Int, x: Int) = ((x % n) == 0)
scala> modN(5, _ : Int)
res0: Int => Boolean = <function1>

Nous obtenons donc un partiellement appliquée <function1> qui prend un Int, car nous avons déjà donné le premier entier. So far So good. Maintenant, pour lancer:

scala> def modNCurried(n: Int)(x: Int) = ((x % n) == 0)

Avec cette notation, vous pensez naïvement attendons la suite du travail:

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

Donc les multiples paramètres de la liste de notation ne semble pas vraiment à créer un curry de fonction (assumingly pour éviter une surcharge inutile), mais attend que vous pour indiquer explicitement que vous voulez au curry (la notation a quelques autres avantages en tant que bien):

scala> modNCurried(5) _
res24: Int => Boolean = <function1>

Ce qui est exactement la même chose que nous avons eu avant, donc pas de différence ici, sauf pour la notation. Un autre exemple:

scala> modN _
res35: (Int, Int) => Boolean = <function2>

scala> modNCurried _
res36: Int => (Int => Boolean) = <function1>

Cela démontre comment partiellement l'application d'une fonction "normale" des résultats dans une fonction qui prend tous les paramètres, tandis que partiellement l'application d'une fonction avec plusieurs paramètre listes crée une chaîne de fonctions, un par liste de paramètres qui, de retour d'une nouvelle fonction:

scala> def foo(a:Int, b:Int)(x:Int)(y:Int) = a * b + x - y
scala> foo _
res42: (Int, Int) => Int => (Int => Int) = <function2>

scala> res42(5)
<console>:10: error: not enough arguments for method apply: (v1: Int, v2: Int)Int => (Int => Int) in trait Function2.
Unspecified value parameter v2.

Comme vous pouvez le voir, parce que le premier paramètre de la liste d' foo a deux paramètres, la première fonction dans le curry de la chaîne dispose de deux paramètres.


En résumé, l'application partielle de fonctions ne sont pas vraiment différentes de la forme des fonctions curryfiées en termes de fonctionnalité. C'est facile à vérifier étant donné que vous pouvez convertir n'importe quelle fonction pour un au curry:

scala> (modN _).curried
res45: Int => (Int => Boolean) = <function1

scala> modNCurried _
res46: Int => (Int => Boolean) = <function1>

Post Scriptum

Note: La raison pour laquelle votre exemple println(filter(nums, modN(2)) fonctionne sans le trait de soulignement après modN(2) semble être que le compilateur Scala suppose simplement que le trait de soulignement comme une commodité pour le programmeur.


Plus: Comme @asflierl a souligné à juste titre, de la Scala ne semblent pas être en mesure de déduire le type partiellement l'application "normale" fonctions:

scala> modN(5, _)
<console>:9: error: missing parameter type for expanded function ((x$1) => modN(5, x$1))

Alors que l'information est disponible pour les fonctions de l'écrit à l'aide de multiples paramètres de la liste de notation:

scala> modNCurried(5) _
res3: Int => Boolean = <function1>

Cela répond montre comment cela peut être très utile.

20voto

Don Stewart Points 94361

Nourrissage est à faire avec les tuples: transformer une fonction qui prend un n-uplet d'argument dans un qui prend n séparer les arguments, et vice versa. Se souvenir c'est la clé de la distinction curry vs application partielle, même dans les langues qui ne sont pas proprement soutien de nourrissage.

curry :: ((a, b) -> c) -> a -> b -> c 
   -- curry converts a function that takes all args in a tuple
   -- into one that takes separate arguments

uncurry :: (a -> b -> c) -> (a, b) -> c
   -- uncurry converts a function of separate args into a function on pairs.

L'application partielle est la capacité à appliquer une fonction à certains arguments, ce qui donne une nouvelle fonction pour le reste des arguments.

Il est facile à retenir si vous pensez juste nourrissage est la transformation à faire avec des n-uplets.

Dans les langues qui sont au curry par défaut (comme Haskell) la différence est claire: vous devez réellement faire quelque chose pour passer des arguments dans un tuple. Mais la plupart des autres langues, y compris Scala, sont uncurried par défaut -- tous les arguments sont passés comme des n-uplets, afin de curry/uncurry est beaucoup moins utile, et moins évident. Et même les gens finissent par croire que l'application partielle et de nourrissage sont la même chose, seulement parce qu'ils ne peuvent pas représenter des fonctions curryfiées facilement!

2voto

lcn Points 379

Multivariables fonction:

def modN(n: Int, x: Int) = ((x % n) == 0)

Nourrissage (ou le curry de la fonction):

def modNCurried(n: Int)(x: Int) = ((x % n) == 0)

Il n'est donc pas appliquée partiellement fonction qui est comparable à lancer. C'est la fonction multivariable. Ce qui est comparable à l'application partielle de la fonction est l'invocation résultat d'un curry de fonction, qui est une fonction avec la même liste de paramètres que l'application partielle de la fonction.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X