desembalaje de scala tuple

91

Sé que esta pregunta ha surgido muchas veces de diferentes maneras. Pero todavía no lo tengo claro. ¿Hay alguna manera de lograr lo siguiente?

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
}
explorar
fuente
11
¿Qué pasa si foo es el constructor de alguna clase?
scout

Respuestas:

107

Es un procedimiento de dos pasos. Primero convierta foo en una función, luego llame a tupled para que sea una función de una tupla.

(foo _).tupled(getParams)
Dave Griffith
fuente
3
¿No sería más limpio si Scala solo pensara en los argumentos como tuplas para empezar?
Henry Story
12
Sí, sería mucho más limpio si Scala unificara su manejo de tuplas y listas de argumentos. Por lo que he oído, hay muchos casos extremos no obvios que necesitarían un manejo cuidadoso para que eso suceda. Hasta donde yo sé, la unificación de tuplas y listas de argumentos no está en la hoja de ruta de Scala en este momento.
Dave Griffith
2
Solo para agregar, si foo es el método de fábrica del objeto complementario, se podría usar (Foo.apply _). Tupled (getParams)
RAbraham
55

@ dave-griffith está seguro.

También puedes llamar al:

Function.tupled(foo _)

Si quieres adentrarte en un territorio de "mucha más información de la que pedí", también hay métodos integrados en funciones parcialmente aplicadas (y otras Function) para currying. Algunos ejemplos de entrada / salida:

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>

Donde la versión curry se invoca con múltiples listas de argumentos:

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

Por último, también puede deshacer / deshacer el tuplo si es necesario. Functiontiene incorporados para esto:

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>

Brendan W. McAdams
fuente
20

Function.tupled(foo _)(getParams)o el sugerido por Dave.

EDITAR:

Para responder a su comentario:

¿Qué pasa si foo es el constructor de alguna clase?

En ese caso, este truco no funcionará.

Puede escribir un método de fábrica en el objeto complementario de su clase y luego obtener la versión tuplicada de su applymétodo utilizando una de las técnicas mencionadas anteriormente.

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 obtienes un objeto complementario con un applymétodo gratis y, por lo tanto, esta técnica es más conveniente para usar con case classes.

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

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

Sé que es mucha duplicación de código, pero por desgracia ... ¡no tenemos macros (todavía)! ;)

desaparecido faktor
fuente
3
En el último ejemplo aquí, podría recortar un poco ... Los objetos complementarios para las clases de casos siempre extienden el rasgo FunctionN apropiado. Entonces, la última línea podría ser Person.tupled(("Rahul", "G")) Es útil hacer esto también en objetos complementarios escritos a mano.
David Winslow
3

Aprecio algunas de las otras respuestas que estaban más cerca de lo que solicitó, pero me resultó más fácil para un proyecto actual agregar otra función que convierte los parámetros de tupla en los parámetros de división:

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

Ahora, puede implementar foo y hacer que tome un parámetro de la clase Tuple2 como tal.

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)
Rejinderi
fuente