Scala fonctions courantes et fonctions partiellement appliquées

je me rends compte qu'il y a plusieurs questions sur ce que fonctions courantes et partiellement appliquées sont, mais je me demande comment ils sont différents. Comme exemple simple, voici une fonction curried pour trouver des nombres pairs:

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 ce qui suit pour utiliser ceci:

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

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

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

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

qui renvoie aussi: 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?

81
demandé sur ROMANIA_engineer 2013-01-14 03:32:45

4 réponses

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

en termes de fonctionnalité, il ne semble pas y avoir de grande différence. 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 <function1> partiellement appliqué qui prend un Int , parce que nous lui avons déjà donné le premier entier. So far So good. Maintenant au currying:

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

avec cette note, vous vous attendez naïvement à ce que les suivants fonctionnent:

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)

ainsi la liste de paramètres multiples notation ne semble pas vraiment créer une fonction curried tout de suite (présumément pour éviter les frais généraux inutiles) mais attend que vous déclarez explicitement que vous voulez qu'il curried (la notation a quelque autres avantages ainsi):

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

ce qui est exactement la même chose qu'avant, donc aucune 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 l'application partielle d'une fonction" normale "résulte en une fonction qui prend tous les paramètres, alors que l'application partielle d'une fonction avec des listes de paramètres multiples crée une chaîne de fonctions, un par liste de paramètres qui, tous renvoient 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 la première liste de paramètres de foo a deux paramètres, la première fonction dans la chaîne courbée a deux paramètres.


en résumé, les fonctions partiellement appliquées ne sont pas vraiment différentes des fonctions courantes en termes de fonctionnalité. Ceci est facilement vérifié étant donné que vous pouvez convertir n'importe quelle fonction en une fonction 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 underscore après modN(2) semble être que le compilateur Scala suppose simplement que underscore comme une commodité pour le programmeur.


ajout: comme @asflierl l'a correctement souligné, Scala ne semble pas être en mesure de déduire le type en appliquant partiellement fonctions "normales":

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

attendu que cette information est disponible pour les fonctions écrites en utilisant la notation de la liste de paramètres multiples:

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

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

86
répondu fresskoma 2017-05-23 11:47:02

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

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.

application partielle est la capacité de appliquer une fonction à certains arguments, donnant une nouvelle fonction pour les arguments restants .

il est facile de se rappeler si vous pensez que currying est la transformation à faire avec les tuples.

dans les langues qui sont gérées 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 le Scala, ne sont pas traitées par défaut -- tous les args sont passés sous forme de tuples, donc curry/uncurry est beaucoup moins utile, et moins évident. Et les gens finissent même par penser cette application partielle et currying sont la même chose -- juste parce qu'ils ne peuvent pas représenter des fonctions curried facilement!

19
répondu Don Stewart 2013-01-14 14:25:15

fonction Multivariable:

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

Nourrissage (ou le curry de fonction):

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

donc ce n'est pas fonction partiellement appliquée qui est comparable à currying. C'est la fonction multivariable. Ce qui est comparable à une fonction partiellement appliquée est le résultat de l'invocation d'une fonction curried, qui est une fonction avec la même Liste de paramètres que la fonction partiellement appliquée A.

2
répondu lcn 2014-07-14 20:49:00

juste pour clarifier sur le dernier point

ajout: comme @asflierl l'a correctement souligné, Scala ne semble pas pour pouvoir déduire le type en appliquant partiellement "normal"" fonctions:

Scala peut déduire des types si tous les paramètres sont des caractères génériques mais pas quand certains d'entre eux sont spécifiés et d'autres pas.

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

scala> modN(1,_)
<console>:13: error: missing parameter type for expanded function ((x) => modN(1, x))
       modN(1,_)
              ^
0
répondu Sud 2016-08-11 18:00:06