Rozpakowywanie krotki scala

95

Wiem, że to pytanie pojawiało się wiele razy na różne sposoby. Ale nadal nie jest to dla mnie jasne. Czy jest sposób, aby osiągnąć następujące cele.

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
}
zwiadowca
źródło
11
A co, jeśli foo jest konstruktorem jakiejś klasy?
zwiad
Możliwy duplikat Jak zastosować funkcję do krotki?
Suma,

Odpowiedzi:

107

To dwuetapowa procedura. Najpierw zamień foo w funkcję, a następnie wywołaj na niej krotkę, aby stała się funkcją krotki.

(foo _).tupled(getParams)
Dave Griffith
źródło
3
Czy nie byłoby czystsze, gdyby Scala na początku pomyślał o argumentach jako o krotkach?
Henry Story,
12
Tak, byłoby dużo czystsze, gdyby Scala ujednoliciła obsługę krotek i list argumentów. Z tego, co słyszałem, jest wiele nieoczywistych skrajnych przypadków, które wymagałyby ostrożnego traktowania, aby tak się stało. O ile wiem, ujednolicenie krotek i list argumentów nie znajduje się w tej chwili na mapie drogowej Scala.
Dave Griffith
2
Wystarczy dodać, że jeśli foo jest fabryczną metodą obiektu towarzyszącego, można użyć (Foo.apply _). Tupled (getParams)
RAbraham
56

@ dave-griffith nie żyje.

Możesz również zadzwonić:

Function.tupled(foo _)

Jeśli chcesz zagłębić się w „dużo więcej informacji niż prosiłem”, istnieją również metody wbudowane w częściowo stosowane funkcje (i tak dalej Function) curry. Kilka przykładów wejścia / wyjścia:

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>

W którym wersja curried jest wywoływana z wieloma listami argumentów:

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

Wreszcie, w razie potrzeby możesz również rozluźnić / rozluźnić. Functionma wbudowane do tego:

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
źródło
20

Function.tupled(foo _)(getParams)lub ten zasugerowany przez Dave'a.

EDYTOWAĆ:

Aby odpowiedzieć na Twój komentarz:

A co jeśli foo jest konstruktorem jakiejś klasy?

W takim przypadku ta sztuczka nie zadziała.

Możesz napisać metodę fabryczną w obiekcie towarzyszącym swojej klasy, a następnie uzyskać zwiniętą wersję jej applymetody przy użyciu jednej z wyżej wymienionych technik.

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

Dzięki case classes otrzymujesz obiekt towarzyszący z applymetodą za darmo, a zatem ta technika jest wygodniejsza w użyciu z case classes.

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

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

Wiem, że to dużo duplikatów kodu, ale niestety ... nie mamy (jeszcze) makr! ;)

missingfaktor
źródło
3
W ostatnim przykładzie tutaj można by trochę zgolić ... Obiekty towarzyszące dla klas przypadków zawsze rozszerzają odpowiednią cechę FunctionN. Więc ostatnia linijka mogłaby brzmieć Person.tupled(("Rahul", "G")) . Przydatne jest zrobienie tego również w odręcznych obiektach towarzyszących.
David Winslow
3

Doceniam niektóre inne odpowiedzi, które były bliższe temu, o co prosiłeś, ale w bieżącym projekcie łatwiej było dodać kolejną funkcję, która konwertuje parametry krotki na parametry podziału:

def originalFunc(a: A, b: B): C = ...
def wrapperFunc(ab: (A, B)): C = (originalFunc _).tupled(ab)
xen
źródło
1

Teraz możesz zaimplementować foo i sprawić, by przyjmował parametr klasy Tuple2 w ten sposób.

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
źródło