déballage de scala tuple


91

Je sais que cette question a été soulevée plusieurs fois de différentes manières. Mais ce n'est toujours pas clair pour moi. Y a-t-il un moyen de réaliser ce qui suit.

def foo(a:Int, b:Int) = {}

foo(a,b) //right way to invoke foo

foo(getParams) // is there a way to get this working without explicitly unpacking the tuple??

def getParams = {
   //Some calculations
   (a,b)  //where a & b are Int
}

11
Et si foo était le constructeur d'une classe?
scout

Réponses:


107

C'est une procédure en deux étapes. Commencez par transformer foo en fonction, puis appelez tupled dessus pour en faire une fonction d'un tuple.

(foo _).tupled(getParams)

3
Ne serait-il pas plus propre si Scala pensait simplement aux arguments comme des tuples pour commencer?
Henry Story

12
Oui, ce serait beaucoup plus propre si Scala unifiait sa gestion des tuples et des listes d'arguments. D'après ce que j'ai entendu, il existe de nombreux cas marginaux non évidents qui nécessiteraient une manipulation minutieuse pour y parvenir. Autant que je sache, l'unification des tuples et des listes d'arguments n'est pas sur la feuille de route de Scala pour le moment.
Dave Griffith

2
Juste pour ajouter, si foo est la méthode d'usine de l'objet compagnon, on pourrait utiliser (Foo.apply _). Tupled (getParams)
RAbraham

55

@ dave-griffith est mort.

Vous pouvez également appeler:

Function.tupled(foo _)

Si vous voulez vous promener dans le territoire "beaucoup plus d'informations que ce que j'ai demandé", il existe également des méthodes intégrées dans des fonctions partiellement appliquées (et sur Function) pour le curry. Quelques exemples d'entrées / sorties:

scala> def foo(x: Int, y: Double) = x * y
foo: (x: Int,y: Double)Double

scala> foo _
res0: (Int, Double) => Double = <function2>

scala> foo _ tupled
res1: ((Int, Double)) => Double = <function1>

scala> foo _ curried
res2: (Int) => (Double) => Double = <function1>

scala> Function.tupled(foo _)
res3: ((Int, Double)) => Double = <function1>

// Function.curried is deprecated
scala> Function.curried(foo _)
warning: there were deprecation warnings; re-run with -deprecation for details
res6: (Int) => (Double) => Double = <function1>

Dans lequel la version curry est appelée avec plusieurs listes d'arguments:

scala> val c = foo _ curried
c: (Int) => (Double) => Double = <function1>

scala> c(5)
res13: (Double) => Double = <function1>

scala> c(5)(10)
res14: Double = 50.0

Enfin, vous pouvez également décortiquer / décompresser si nécessaire. Functiona intégré pour cela:

scala> val f = foo _ tupled
f: ((Int, Double)) => Double = <function1>

scala> val c = foo _ curried
c: (Int) => (Double) => Double = <function1>

scala> Function.uncurried(c)
res9: (Int, Double) => Double = <function2>

scala> Function.untupled(f)
res12: (Int, Double) => Double = <function2>


20

Function.tupled(foo _)(getParams)ou celui suggéré par Dave.

ÉDITER:

Pour répondre à votre commentaire:

Et si foo était le constructeur d'une classe?

Dans ce cas, cette astuce ne fonctionnera pas.

Vous pouvez écrire une méthode de fabrique dans l'objet compagnon de votre classe, puis obtenir la version tuplée de sa applyméthode en utilisant l'une des techniques susmentionnées.

scala> class Person(firstName: String, lastName: String) {
     |   override def toString = firstName + " " + lastName
     | }
defined class Person

scala> object Person {
     |   def apply(firstName: String, lastName: String) = new Person(firstName, lastName)
     | }
defined module Person

scala> (Person.apply _).tupled(("Rahul", "G"))
res17: Person = Rahul G

Avec case classes, vous obtenez un objet compagnon avec une applyméthode gratuitement, et cette technique est donc plus pratique à utiliser avec case classes.

scala> case class Person(firstName: String, lastName: String)
defined class Person

scala> Person.tupled(("Rahul", "G"))
res18: Person = Person(Rahul,G)

Je sais que c'est beaucoup de duplication de code mais hélas ... nous n'avons pas (encore) de macros! ;)


3
Dans le dernier exemple ici, vous pourriez raser un peu ... Les objets compagnons pour les classes de cas étendent toujours le trait FunctionN approprié. Donc, la dernière ligne pourrait être Person.tupled(("Rahul", "G")) Il est également pratique de le faire dans des objets compagnons écrits à la main.
David Winslow

3

J'apprécie certaines des autres réponses qui étaient plus proches de ce que vous avez demandé, mais j'ai trouvé plus facile pour un projet actuel d'ajouter une autre fonction qui convertit les paramètres de tuple en paramètres de division:

def originalFunc(a: A, b: B): C = ...
def wrapperFunc(ab: (A, B)): C = (originalFunc _).tupled(ab)

1

Maintenant, vous pouvez implémenter foo et lui faire prendre un paramètre de la classe Tuple2 comme ça.

def foo(t: Tuple2[Int, Int]) = {
  println("Hello " + t._1 + t._2)
  "Makes no sense but ok!"
}

def getParams = {
  //Some calculations
  val a = 1;
  val b = 2;
  (a, b) //where a & b are Int
}

// So you can do this!
foo(getParams)
// With that said, you can also do this!
foo(1, 3)
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.