Это невозможно (кроме «вручную») AFAIK. Учитывая def toTuple(x: List[Int]): R, каким должен быть тип R?
7
Если это не нужно делать для кортежа произвольного размера (например, как гипотетический метод, представленный выше), рассмотрите x match { case a :: b :: c :: Nil => (a, b, c); case _ => (0, 0, 0) }и обратите внимание, что результирующий тип зафиксирован наTuple3[Int,Int,Int]
2
Хотя то, что вы хотите сделать, невозможно List, вы можете изучить HListтип Shapeless , который позволяет преобразовывать кортежи и обратно ( github.com/milessabin/… ). Возможно, это применимо к вашему варианту использования.
Ответы:
59
Вы не можете сделать это безопасным способом. Зачем? Потому что, как правило, мы не можем узнать длину списка до времени выполнения. Но «длина» кортежа должна быть закодирована в его типе и, следовательно, известна во время компиляции. Например, (1,'a',true)имеет тип (Int, Char, Boolean), для которого сахар Tuple3[Int, Char, Boolean]. Причина, по которой кортежи имеют это ограничение, заключается в том, что они должны иметь возможность обрабатывать неоднородные типы.
Есть ли список элементов одного и того же типа фиксированного размера? Не импортировать нестандартные библиотеки, вроде shapeless, конечно.
1
@davips, насколько я знаю, но его достаточно легко сделать самому, напримерcase class Vec3[A](_1: A, _2: A, _3: A)
Том Крокетт
Это будет «кортеж» элементов одного типа, например, я не могу применить к нему карту.
1
@davips, как и в случае с любым новым типом данных, вам нужно будет определить, как mapдля него работает и т. д.
Том Крокетт
1
Такое ограничение кажется ярким показателем бесполезности безопасности типов больше, чем что-либо еще. Это напоминает мне цитату «пустой набор имеет много интересных свойств».
Эли
58
Вы можете сделать это с помощью экстракторов scala и сопоставления с образцом ( ссылка ):
val x = List(1, 2, 3)
val t = x match {
caseList(a, b, c) => (a, b, c)
}
Что возвращает кортеж
t: (Int, Int, Int) = (1,2,3)
Кроме того, вы можете использовать подстановочный знак, если не уверены в размере списка.
val t = x match {
caseList(a, b, c, _*) => (a, b, c)
}
В контексте этого решения жалобы на безопасность типов означают, что вы можете получить MatchError во время выполнения. Если хотите, можете попробовать перехватить эту ошибку и что-нибудь предпринять, если у List неправильная длина. Еще одна приятная особенность этого решения заключается в том, что вывод не обязательно должен быть кортежем, это может быть один из ваших собственных пользовательских классов или какое-то преобразование чисел. Я не думаю, что вы можете сделать это с не-списками (так что вам может потребоваться выполнить операцию .toList для ввода).
Джим Пиварски
Вы можете сделать это с любым типом последовательности Scala, используя синтаксис + :. Кроме того, не забудьте добавить
import shapeless._
import syntax.std.traversable._
val x = List(1, 2, 3)
val xHList = x.toHList[Int::Int::Int::HNil]
val t = xHList.get.tupled
Примечание: компилятору нужна некоторая информация о типе для преобразования списка в HList, что является причиной, по которой вам необходимо передать информацию о типе в toHListметод.
Shapeless 2.0 изменил синтаксис. Вот обновленное решение с использованием shapeless.
import shapeless._
importHList._
import syntax.std.traversable._
val x = List(1, 2, 3)
val y = x.toHList[Int::Int::Int::HNil]
val z = y.get.tupled
Основная проблема в том, что тип для .toHList нужно указывать заранее. В более общем плане, поскольку кортежи ограничены по своей арности, для проектирования вашего программного обеспечения может быть лучше другое решение.
Тем не менее, если вы создаете список статически, рассмотрите решение, подобное этому, также с использованием shapeless. Здесь мы создаем HList напрямую, и тип доступен во время компиляции. Помните, что HList имеет функции как типа List, так и типа Tuple. т.е. он может иметь элементы с разными типами, такие как Tuple, и может отображаться среди других операций, таких как стандартные коллекции. К спискам H нужно время, чтобы привыкнуть к ним, так что действуйте медленно, если вы новичок.
@javadba Я искал, как реализовать listToTuple (), но в итоге мне это не понадобилось. Синтаксический сахар списка решил мою проблему.
Peter L
Есть еще один синтаксис, который, на мой взгляд, выглядит немного лучше:val List(c1, c2, c3) = myList
Колмар
7
Вы не можете сделать это безопасным способом. В Scala списки - это последовательности элементов некоторого типа произвольной длины . Насколько известно системе типов, это xможет быть список произвольной длины.
Напротив, арность кортежа должна быть известна во время компиляции. Присвоение xкортежному типу нарушило бы гарантии безопасности системы типов.
Можно было бы вручную закодировать функцию, которая преобразует списки до 22 элементов и генерирует исключение для больших списков. Поддержка шаблонов в Scala, которая появится в ближайшее время, сделает это более кратким. Но это было бы уродливым взломом.
Это типобезопасно: вы получаете None, если размер кортежа неверен, но размер кортежа должен быть литералом или final val(чтобы быть преобразованным в shapeless.Nat).
Кажется, это не работает для размеров больше 22, по крайней мере, для
shapeless_2.11
@kfkhalili Да, и это не бесформенное ограничение. Сама Scala 2 не позволяет создавать кортежи с более чем 22 элементами, поэтому невозможно преобразовать список большего размера в кортеж с помощью любого метода.
Колмар
4
Использование сопоставления с образцом:
val intTuple = List (1,2,3) соответствует {case List (a, b, c) => (a, b, c)}
Отвечая на этот старый (более 6 лет) вопрос, часто бывает полезно указать, чем ваш ответ отличается от всех (12) старых ответов, которые уже были отправлены. В частности, ваш ответ применим только в том случае, если длина List/ Tupleявляется известной константой.
jwvh
Спасибо @ jwvh, рассмотрим ваши комментарии в будущем.
Майкл Олафисой
2
Пост 2015 г. Чтобы ответ Тома Крокетта был более проясненным , вот реальный пример.
Сначала запуталась. Потому что я пришел с Python, где вы можете просто делать tuple(list(1,2,3)).
Не хватает языка Scala? (ответ - это не о Scala или Python, а о статическом и динамическом типах.)
Это заставляет меня пытаться понять, почему Scala не может этого сделать.
В следующем примере кода реализуется toTupleметод с типобезопасным toTupleNи небезопасным типом toTuple.
toTupleСпособ получить информацию типа длиной во время выполнения, то есть информации о типе длиной во время компиляции, так что возвращаемый тип , Productкоторый очень похожи на Пайтоне в tupleсамом деле (без типа в каждом положении, и не длинами типов).
Таким образом, возможны ошибки времени выполнения, такие как несоответствие типов или IndexOutOfBoundException. (так что удобное преобразование списка в кортеж в Python - это не бесплатный обед.)
Напротив, именно предоставленная пользователем информация о длине делает toTupleNбезопасным время компиляции.
implicitclassEnrichedWithToTuple[A](elements: Seq[A]) {
deftoTuple: Product = elements.length match {
case2 => toTuple2
case3 => toTuple3
}
deftoTuple2= elements match {caseSeq(a, b) => (a, b) }
deftoTuple3= elements match {caseSeq(a, b, c) => (a, b, c) }
}
val product = List(1, 2, 3).toTuple
product.productElement(5) //runtime IndexOutOfBoundException, Bad ! val tuple = List(1, 2, 3).toTuple3
tuple._5 //compiler error, Good!
def toTuple(x: List[Int]): R
, каким должен быть тип R?x match { case a :: b :: c :: Nil => (a, b, c); case _ => (0, 0, 0) }
и обратите внимание, что результирующий тип зафиксирован наTuple3[Int,Int,Int]
List
, вы можете изучитьHList
тип Shapeless , который позволяет преобразовывать кортежи и обратно ( github.com/milessabin/… ). Возможно, это применимо к вашему варианту использования.Ответы:
Вы не можете сделать это безопасным способом. Зачем? Потому что, как правило, мы не можем узнать длину списка до времени выполнения. Но «длина» кортежа должна быть закодирована в его типе и, следовательно, известна во время компиляции. Например,
(1,'a',true)
имеет тип(Int, Char, Boolean)
, для которого сахарTuple3[Int, Char, Boolean]
. Причина, по которой кортежи имеют это ограничение, заключается в том, что они должны иметь возможность обрабатывать неоднородные типы.источник
case class Vec3[A](_1: A, _2: A, _3: A)
map
для него работает и т. д.Вы можете сделать это с помощью экстракторов scala и сопоставления с образцом ( ссылка ):
val x = List(1, 2, 3) val t = x match { case List(a, b, c) => (a, b, c) }
Что возвращает кортеж
t: (Int, Int, Int) = (1,2,3)
Кроме того, вы можете использовать подстановочный знак, если не уверены в размере списка.
val t = x match { case List(a, b, c, _*) => (a, b, c) }
источник
пример использования shapeless :
import shapeless._ import syntax.std.traversable._ val x = List(1, 2, 3) val xHList = x.toHList[Int::Int::Int::HNil] val t = xHList.get.tupled
Примечание: компилятору нужна некоторая информация о типе для преобразования списка в HList, что является причиной, по которой вам необходимо передать информацию о типе в
toHList
метод.источник
Shapeless 2.0 изменил синтаксис. Вот обновленное решение с использованием shapeless.
import shapeless._ import HList._ import syntax.std.traversable._ val x = List(1, 2, 3) val y = x.toHList[Int::Int::Int::HNil] val z = y.get.tupled
Основная проблема в том, что тип для .toHList нужно указывать заранее. В более общем плане, поскольку кортежи ограничены по своей арности, для проектирования вашего программного обеспечения может быть лучше другое решение.
Тем не менее, если вы создаете список статически, рассмотрите решение, подобное этому, также с использованием shapeless. Здесь мы создаем HList напрямую, и тип доступен во время компиляции. Помните, что HList имеет функции как типа List, так и типа Tuple. т.е. он может иметь элементы с разными типами, такие как Tuple, и может отображаться среди других операций, таких как стандартные коллекции. К спискам H нужно время, чтобы привыкнуть к ним, так что действуйте медленно, если вы новичок.
scala> import shapeless._ import shapeless._ scala> import HList._ import HList._ scala> val hlist = "z" :: 6 :: "b" :: true :: HNil hlist: shapeless.::[String,shapeless.::[Int,shapeless.::[String,shapeless.::[Boolean,shapeless.HNil]]]] = z :: 6 :: b :: true :: HNil scala> val tup = hlist.tupled tup: (String, Int, String, Boolean) = (z,6,b,true) scala> tup res0: (String, Int, String, Boolean) = (z,6,b,true)
источник
Несмотря на простоту и непригодность для списков любой длины, он безопасен по типу и в большинстве случаев дает ответ:
val list = List('a','b') val tuple = list(0) -> list(1) val list = List('a','b','c') val tuple = (list(0), list(1), list(2))
Другая возможность, когда вы не хотите называть список или повторять его (я надеюсь, что кто-то может показать способ избежать частей Seq / head):
val tuple = Seq(List('a','b')).map(tup => tup(0) -> tup(1)).head val tuple = Seq(List('a','b','c')).map(tup => (tup(0), tup(1), tup(2))).head
источник
FWIW, я хотел, чтобы кортеж инициализировал ряд полей, и хотел использовать синтаксический сахар присваивания кортежа. НАПРИМЕР:
val (c1, c2, c3) = listToTuple(myList)
Оказывается, есть и синтаксический сахар для присвоения содержимого списка ...
val c1 :: c2 :: c3 :: Nil = myList
Так что нет необходимости в кортежах, если у вас такая же проблема.
источник
val List(c1, c2, c3) = myList
Вы не можете сделать это безопасным способом. В Scala списки - это последовательности элементов некоторого типа произвольной длины . Насколько известно системе типов, это
x
может быть список произвольной длины.Напротив, арность кортежа должна быть известна во время компиляции. Присвоение
x
кортежному типу нарушило бы гарантии безопасности системы типов.Фактически, по техническим причинам, кортежи Scala были ограничены 22 элементами , но ограничение больше не существует в 2.11. Ограничение класса case было снято в 2.11. https://github.com/scala/scala/pull/2305
Можно было бы вручную закодировать функцию, которая преобразует списки до 22 элементов и генерирует исключение для больших списков. Поддержка шаблонов в Scala, которая появится в ближайшее время, сделает это более кратким. Но это было бы уродливым взломом.
источник
Если вы уверены, что ваш list.size <23, используйте его:
def listToTuple[A <: Object](list:List[A]):Product = { val class = Class.forName("scala.Tuple" + list.size) class.getConstructors.apply(0).newInstance(list:_*).asInstanceOf[Product] } listToTuple: [A <: java.lang.Object](list: List[A])Product scala> listToTuple(List("Scala", "Smart")) res15: Product = (Scala,Smart)
источник
Это также можно сделать
shapeless
с меньшим количеством шаблонов, используяSized
:scala> import shapeless._ scala> import shapeless.syntax.sized._ scala> val x = List(1, 2, 3) x: List[Int] = List(1, 2, 3) scala> x.sized(3).map(_.tupled) res1: Option[(Int, Int, Int)] = Some((1,2,3))
Это типобезопасно: вы получаете
None
, если размер кортежа неверен, но размер кортежа должен быть литералом илиfinal val
(чтобы быть преобразованным вshapeless.Nat
).источник
Использование сопоставления с образцом:
val intTuple = List (1,2,3) соответствует {case List (a, b, c) => (a, b, c)}
источник
List
/Tuple
является известной константой.Пост 2015 г. Чтобы ответ Тома Крокетта был более проясненным , вот реальный пример.
Сначала запуталась. Потому что я пришел с Python, где вы можете просто делать
tuple(list(1,2,3))
.Не хватает языка Scala? (ответ - это не о Scala или Python, а о статическом и динамическом типах.)
Это заставляет меня пытаться понять, почему Scala не может этого сделать.
В следующем примере кода реализуется
toTuple
метод с типобезопаснымtoTupleN
и небезопасным типомtoTuple
.toTuple
Способ получить информацию типа длиной во время выполнения, то есть информации о типе длиной во время компиляции, так что возвращаемый тип ,Product
который очень похожи на Пайтоне вtuple
самом деле (без типа в каждом положении, и не длинами типов).Таким образом, возможны ошибки времени выполнения, такие как несоответствие типов или
IndexOutOfBoundException
. (так что удобное преобразование списка в кортеж в Python - это не бесплатный обед.)Напротив, именно предоставленная пользователем информация о длине делает
toTupleN
безопасным время компиляции.implicit class EnrichedWithToTuple[A](elements: Seq[A]) { def toTuple: Product = elements.length match { case 2 => toTuple2 case 3 => toTuple3 } def toTuple2 = elements match {case Seq(a, b) => (a, b) } def toTuple3 = elements match {case Seq(a, b, c) => (a, b, c) } } val product = List(1, 2, 3).toTuple product.productElement(5) //runtime IndexOutOfBoundException, Bad ! val tuple = List(1, 2, 3).toTuple3 tuple._5 //compiler error, Good!
источник
ты можешь сделать это либо
перебирая список и применяя каждый элемент один за другим.
val xs: Seq[Any] = List(1:Int, 2.0:Double, "3":String) val t: (Int,Double,String) = xs.foldLeft((Tuple3[Int,Double,String] _).curried:Any)({ case (f,x) => f.asInstanceOf[Any=>Any](x) }).asInstanceOf[(Int,Double,String)]
источник
насколько у вас есть тип:
val x: List[Int] = List(1, 2, 3) def doSomething(a:Int *) doSomething(x:_*)
источник