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
}

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)

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>


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! ;)


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)

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)
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.