2016-10-18 1 views
1

Au début, je l'avais cru que l'utilisation des underscores pour faire des fermetures (par exemple println _) étaient juste un raccourci pour l'aide d'une flèche (par exemple x => println x), mais j'appris récemment que vous pouvez également effectuer les opérations suivantes:Dans Scala, quelles sont les règles pour faire des fermetures avec des underscores?

def f(a: Int, b: Int) = a + 2 * b 
List(1, 2, 3).reduce(f _) 

Compte tenu mes hypothèses passées, f _ ressemble à une fermeture qui accepte exactement un argument et passe exactement un argument à f. J'ai supposé qu'il me dirait qu'il ne pourrait pas compiler parce que f attend deux arguments, et reduce devrait s'attendre à une fonction avec deux arguments. Mais cela fonctionne comme si j'avais écrit:

def f(a: Int, b: Int) = a + 2 * b 
List(1, 2, 3).reduce((x, y) => f(x, y)) 

Que se passe-t-il ici? Quelles sont les règles pour créer des fermetures avec des underscores?

+2

Dans votre cas, 'f _' est un type de fonction utilisant le mécanisme d'expansion eta. Autrement dit, il traduit la méthode 'f (a: Int, b: Int): Int' en' (Int, Int) => Int' (c'est-à-dire 'Function2 [Int, Int, Int]'), qui est un type valide à fournir pour réduire. –

+0

(aussi comme une note rapide, votre fonction n'est pas associative, ce qui a brisé le contrat requis par réduire) –

+0

@ math4tots aussi, sachez que 'f _' et' f (_) 'sont deux choses différentes. – fxlae

Répondre

3

Rien de spécial ne se passe. La méthode reduce prend une fonction qui prend deux Int s et produit un Int, donc en fournissant un f fonctionne très bien. Notez que lorsque vous dites f _ qui s'étend réellement à x => f x (ou, dans le cas de deux paramètres tels que ici, (x, y) => f(x, y)). Vous pouvez également fournir f qui sera ensuite utilisé directement, sans le wrapper de fonction anonyme supplémentaire.

La transformation d'une méthode en fonction en f _ est appelée eta-expansion (description complète: j'ai écrit cet article). La différence est subtile; function est une valeur, tandis qu'une méthode est, bien, une méthode que vous appelez sur un objet pour lequel elle est définie, par ex. myObject.myMethod. La fonction peut être autonome, être conservée dans des collections, etc. Définir votre méthode f directement comme une fonction serait val f: (Int, Int) => Int = (a: Int, b: Int) => a + b ou, avec l'inférence de type, val f = (a: Int, b: Int) => a + b.

BTW Je ne vois pas comment c'est une fermeture.

+0

Quelle est la différence entre une fonction partiellement appliquée et une expansion eta? Par exemple. 'val k = f _', n'est-ce pas aussi une fonction partiellement appliquée? – ceran

+0

@ceran Non. La fonction partiellement appliquée serait 'f (42, _: Int)'. Ou s'il est curry comme 'def f (a: Int) (b: Int)' alors vous pouvez dire 'f (42) _'. Mais juste dire 'f _' signifie qu'une méthode' f' sera transformée en fonction. Voir également modifier dans ma réponse pour plus de clarification. – slouc

+0

Hm Je ne vois pas la différence. Le livre de Scala d'Odersky indique que dans 'list.foreach (println _)', 'println _' représente une fonction partiellement appliquée. Vous n'avez pas besoin de fournir d'argument du tout. – ceran