scala tuple spacchettamento


91

So che questa domanda è stata sollevata molte volte in modi diversi. Ma non mi è ancora chiaro. C'è un modo per ottenere quanto segue.

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
E se foo fosse il costruttore di una classe?
scout il

Risposte:


107

È una procedura in due fasi. Per prima cosa trasforma foo in una funzione, quindi chiama tupled su di essa per renderlo una funzione di una tupla.

(foo _).tupled(getParams)

3
Non sarebbe più pulito se Scala pensasse agli argomenti solo come tuple per cominciare?
Henry Story

12
Sì, sarebbe molto più pulito se Scala unificasse la sua gestione delle tuple e degli elenchi di argomenti. Da quello che ho sentito, ci sono un sacco di casi limite non ovvi che richiederebbero un'attenta gestione per farlo accadere. Per quanto ne so, l'unificazione di tuple ed elenchi di argomenti non è sulla roadmap di Scala in questo momento.
Dave Griffith

2
Solo per aggiungere, se foo è il metodo di fabbrica dell'oggetto associato, si potrebbe usare (Foo.apply _). Tupled (getParams)
RAbraham

55

@ dave-griffith è morto.

Puoi anche chiamare:

Function.tupled(foo _)

Se vuoi vagare nel territorio "molte più informazioni di quelle che ho chiesto", ci sono anche metodi integrati in funzioni parzialmente applicate (e così via Function) per il curry. Alcuni esempi di input / output:

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>

In cui la versione curry viene invocata con più elenchi di argomenti:

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

Infine, puoi anche annullare / annullare l'associazione, se necessario. Functionha builtin per questo:

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)o quello suggerito da Dave.

MODIFICARE:

Per rispondere al tuo commento:

E se foo fosse il costruttore di una classe?

In tal caso, questo trucco non funzionerà.

Puoi scrivere un metodo factory nell'oggetto companion della tua classe e quindi ottenere la versione tuplicata del suo applymetodo usando una delle tecniche sopra menzionate.

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

Con case classes ottieni un oggetto compagno con un applymetodo gratuitamente, e quindi questa tecnica è più comoda da usare con case classes.

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

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

So che è un sacco di duplicazioni di codice ma ahimè ... non abbiamo (ancora) le macro! ;)


3
Nell'ultimo esempio qui, potresti raderti un po '... Gli oggetti companion per le classi case estendono sempre il tratto FunctionN appropriato. Quindi l'ultima riga potrebbe essere Person.tupled(("Rahul", "G")) È utile farlo anche negli oggetti associati scritti a mano.
David Winslow

3

Apprezzo alcune delle altre risposte che erano più vicine a ciò che hai chiesto, ma ho trovato più facile per un progetto corrente aggiungere un'altra funzione che converte i parametri della tupla nei parametri di divisione:

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

1

Ora puoi implementare foo e fargli prendere un parametro della classe Tuple2 in questo modo.

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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.