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
}

11
A co, jeśli foo jest konstruktorem jakiejś klasy?
zwiad

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)

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>


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


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)

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)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.