descompactação de tupla scala


91

Sei que essa pergunta surgiu muitas vezes de maneiras diferentes. Mas ainda não está claro para mim. Existe uma maneira de conseguir o seguinte.

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 for o construtor de alguma classe?
olheiro de

Respostas:


107

É um procedimento de duas etapas. Primeiro transforme foo em uma função e, em seguida, chame tupled nele para torná-lo uma função de uma tupla.

(foo _).tupled(getParams)

3
Não seria mais limpo se Scala apenas pensasse em argumentos como Tuplas, para começar?
Henry Story

12
Sim, seria muito mais limpo se Scala unificasse seu manuseio de tuplas e listas de argumentos. Pelo que ouvi, há muitos casos extremos não óbvios que precisam de tratamento cuidadoso para que isso aconteça. Pelo que eu sei, a unificação de tuplas e listas de argumentos não está no roteiro do Scala no momento.
Dave Griffith

2
Apenas para adicionar, se foo é o método de fábrica do objeto companheiro, pode-se usar (Foo.apply _). Tupled (getParams)
RAbraham

55

@ dave-griffith está certo.

Você também pode ligar para:

Function.tupled(foo _)

Se você quiser entrar no território de "muito mais informações do que eu pedi", também existem métodos embutidos em funções parcialmente aplicadas (e Functionativadas) para currying. Alguns exemplos de entrada / saída:

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>

Onde a versão curried é invocada com múltiplas 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 fim, você também pode reduzir a pressa / desacelerar, se necessário. Functiontem builtins para isso:

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 aquele sugerido por Dave.

EDITAR:

Para responder ao seu comentário:

E se foo for o construtor de alguma classe?

Nesse caso, esse truque não funcionará.

Você pode escrever um método de fábrica no objeto companheiro de sua classe e, em seguida, obter a versão tuplada de seu applymétodo usando uma das técnicas acima mencionadas.

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

Com case classes, você obtém um objeto companheiro com um applymétodo gratuitamente e, portanto, essa técnica é mais conveniente para usar com case classes.

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

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

Eu sei que é muita duplicação de código, mas, infelizmente ... não temos macros (ainda)! ;)


3
No último exemplo aqui, você poderia economizar um pouco ... Objetos companheiros para classes de caso sempre estendem o traço FunctionN apropriado. Portanto, a última linha pode ser Person.tupled(("Rahul", "G")) É útil fazer isso também em objetos companheiros escritos à mão.
David Winslow

3

Agradeço algumas das outras respostas que foram mais próximas do que você pediu, mas achei mais fácil para um projeto atual adicionar outra função que converte parâmetros de tupla em parâmetros de divisão:

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

1

Agora, você pode implementar foo e torná-lo um parâmetro da classe 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)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.