розпакування кортежу масштабу


91

Я знаю, що це питання неодноразово виникало по-різному. Але мені це досі незрозуміло. Чи є спосіб досягти наступного.

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
Що робити, якщо foo випадково є конструктором якогось класу?
розвідник

Відповіді:


107

Це двоетапна процедура. Спочатку перетворіть foo у функцію, а потім викличте кортеж, щоб зробити його функцією кортежу.

(foo _).tupled(getParams)

3
Чи не було б чистіше, якби Скала для початку просто думала про аргументи як про Туплес?
Henry Story

12
Так, було б набагато чистіше, якби Scala уніфікувала обробку кортежів та списків аргументів. З того, що я чув, є багато неочевидних випадків крайових ситуацій, які потребують обережного поводження, щоб це відбулося. Наскільки мені відомо, наразі уніфікація кортежів та списків аргументів не входить у дорожню карту Scala.
Дейв Гріффіт,

2
Додамо лише, якщо foo є заводським методом об'єкта-супутника, можна було б використовувати (Foo.apply _). Tupled (getParams)
RAbraham

55

@ dave-griffith мертвий.

Ви також можете зателефонувати:

Function.tupled(foo _)

Якщо ви хочете заглянути на територію "набагато більше інформації, ніж я просив", існують також методи, вбудовані в частково застосовані функції (і ввімкнено Function) для каррінгу. Кілька прикладів введення / виводу:

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>

При цьому викликана версія викликається з кількома списками аргументів:

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

Нарешті, ви також можете uncurry / untuple, якщо це необхідно. Functionмає вбудовані для цього:

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)або той, що запропонував Дейв.

РЕДАГУВАТИ:

Щоб відповісти на ваш коментар:

Що робити, якщо foo випадково є конструктором якогось класу?

У цьому випадку цей фокус не спрацює.

Ви можете написати заводський метод у супутньому об’єкті вашого класу, а потім отримати збірку версії його applyметоду, використовуючи один із вищезазначених методів.

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

З case classes ви отримуєте супутній об'єкт із applyметодом безкоштовно, і, отже, цей прийом зручніше використовувати з case classes.

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

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

Я знаю, що це багато дублювання коду, але на жаль ... у нас немає макросів (поки)! ;)


3
В останньому прикладі тут ви могли б трохи поголитися ... Об’єкти-супутники для класів випадків завжди розширюють відповідну ознаку FunctionN. Отже, останній рядок може бути: Person.tupled(("Rahul", "G")) Це зручно робити і в рукописних супутніх об’єктах.
Девід Уінслоу,

3

Я ціную деякі інші відповіді, які були ближче до того, про що ви просили, але мені стало простіше для поточного проекту додати ще одну функцію, яка перетворює параметри кортежу в параметри розділення:

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

1

Тепер ви можете реалізувати foo і змусити його взяти параметр класу Tuple2 ось так.

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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.