Scala Tupel auspacken

90

Ich weiß, dass diese Frage oft auf unterschiedliche Weise gestellt wurde. Aber es ist mir immer noch nicht klar. Gibt es eine Möglichkeit, Folgendes zu erreichen?

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
}
erkunden
quelle
11
Was ist, wenn foo zufällig der Konstruktor einer Klasse ist?
Scout

Antworten:

106

Es ist ein zweistufiges Verfahren. Verwandeln Sie zuerst foo in eine Funktion und rufen Sie dann tupled auf, um es zu einer Funktion eines Tupels zu machen.

(foo _).tupled(getParams)
Dave Griffith
quelle
3
Wäre es nicht sauberer, wenn Scala Argumente zunächst nur als Tupel betrachten würde?
Henry Story
12
Ja, es wäre viel sauberer, wenn Scala den Umgang mit Tupeln und Argumentlisten vereinheitlichen würde. Nach allem, was ich gehört habe, gibt es viele nicht offensichtliche Randfälle, die eine sorgfältige Behandlung erfordern würden, um dies zu erreichen. Soweit ich weiß, steht die Vereinheitlichung von Tupeln und Argumentlisten derzeit nicht auf der Scala-Roadmap.
Dave Griffith
2
Nur um hinzuzufügen, wenn foo die Factory-Methode des Begleitobjekts ist, könnte man (Foo.apply _) verwenden. Tupled (getParams)
RAbraham
55

@ Dave-Griffith ist tot auf.

Sie können auch anrufen:

Function.tupled(foo _)

Wenn Sie in das Gebiet "viel mehr Informationen als gewünscht" eintauchen möchten, gibt es auch Methoden, die in teilweise angewendete Funktionen (und weiter Function) zum Currying integriert sind. Einige Beispiele für Eingabe / Ausgabe:

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>

Wobei die Curry-Version mit mehreren Argumentlisten aufgerufen wird:

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

Schließlich können Sie bei Bedarf auch entkorken / entkoppeln. Functionhat eingebaute dafür:

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
quelle
20

Function.tupled(foo _)(getParams)oder der von Dave vorgeschlagene.

BEARBEITEN:

Um auf Ihren Kommentar zu antworten:

Was ist, wenn foo zufällig der Konstruktor einer Klasse ist?

In diesem Fall funktioniert dieser Trick nicht.

Sie können eine Factory-Methode in das Begleitobjekt Ihrer Klasse schreiben und dann die tupelige Version ihrer applyMethode mithilfe einer der oben genannten Techniken abrufen .

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

Mit case classes erhalten Sie ein Begleitobjekt mit einer applykostenlosen Methode, und daher ist diese Technik mit case classes bequemer zu verwenden .

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

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

Ich weiß, dass das eine Menge Code-Duplizierung ist, aber leider ... haben wir (noch) keine Makros! ;)

fehlender Faktor
quelle
3
Im letzten Beispiel hier könnten Sie sich etwas rasieren ... Companion-Objekte für Fallklassen erweitern immer das entsprechende FunctionN-Merkmal. Die letzte Zeile könnte also sein. Person.tupled(("Rahul", "G")) Es ist praktisch, dies auch in handgeschriebenen Begleitobjekten zu tun.
David Winslow
3

Ich schätze einige der anderen Antworten, die näher an Ihren Anforderungen lagen, aber ich fand es für ein aktuelles Projekt einfacher, eine weitere Funktion hinzuzufügen, die Tupelparameter in geteilte Parameter konvertiert:

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

Jetzt können Sie foo implementieren und dafür sorgen, dass ein Parameter der Tuple2-Klasse wie folgt verwendet wird.

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
quelle