распаковка кортежей в scala


95

Я знаю, что этот вопрос поднимался много раз по-разному. Но мне это все еще непонятно. Есть ли способ добиться следующего.

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
Разве не было бы чище, если бы Scala для начала просто рассматривала аргументы как кортежи?
Генри Стори,

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

2
Просто добавлю, что если foo является фабричным методом сопутствующего объекта, можно использовать (Foo.apply _). Tupled (getParams)
RAbraham

56

@ Дэйв-Гриффит мертв.

Вы также можете позвонить:

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

Наконец, вы также можете развернуть / развернуть, если необходимо. 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
В последнем примере вы могли бы немного сбрить ... Объекты-компаньоны для классов case всегда расширяют соответствующий признак 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.