Как я могу преобразовать список с (скажем) тремя элементами в кортеж размером 3?
Например, допустим, что у меня есть val x = List(1, 2, 3)
, и я хочу преобразовать его в (1, 2, 3)
. Как я могу это сделать?
Как я могу преобразовать список с (скажем) тремя элементами в кортеж размером 3?
Например, допустим, что у меня есть val x = List(1, 2, 3)
, и я хочу преобразовать его в (1, 2, 3)
. Как я могу это сделать?
Вы не можете сделать это по-разному. Зачем? Потому что в общем случае мы не можем знать длину списка до времени выполнения. Но "длина" кортежа должна быть закодирована в своем типе и, следовательно, известна во время компиляции. Например, (1,'a',true)
имеет тип (Int, Char, Boolean)
, который является сахаром для Tuple3[Int, Char, Boolean]
. Причина, по которой кортежи имеют это ограничение, заключается в том, что они должны иметь возможность обрабатывать неоднородные типы.
Вы можете сделать это, используя 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 изменил какой-то синтаксис. Здесь обновленное решение с использованием бесформенного.
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 должен быть указан заранее. В более общем плане, поскольку кортежи ограничены по своей сути, дизайн вашего программного обеспечения может быть лучше использован другим решением.
Тем не менее, если вы создаете список статически, рассмотрите решение, подобное этому, также используя бесформенное. Здесь мы создаем HList напрямую, и тип доступен во время компиляции. Помните, что HList имеет функции из типов List и Tuple. то есть он может иметь элементы с различными типами, такими как Tuple, и может отображаться среди других операций, таких как стандартные коллекции. HLists потратить немного времени, чтобы привыкнуть, хотя так медленно продвигаться, если вы новичок.
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)
FWIW, мне нужен кортеж для инициализации нескольких полей и хотел использовать синтаксический сахар для назначения кортежа. EG:
val (c1, c2, c3) = listToTuple(myList)
Оказывается, есть синтаксический сахар для назначения содержимого списка тоже...
val c1 :: c2 :: c3 :: Nil = myList
Так что нет необходимости в кортежах, если у вас такая же проблема.
Несмотря на простоту и не для списков любой длины, он является типобезопасным и ответ в большинстве случаев:
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
Вы не можете сделать это безопасным способом. В Scala перечислены произвольные последовательности элементов некоторого типа. Насколько известно системе типов, x
может быть списком произвольной длины.
Напротив, арность кортежа должна быть известна во время компиляции. Это нарушит гарантии безопасности системы типов, позволяющие присваивать x
типу кортежа.
Фактически, по техническим причинам Scala tuples были ограничены 22 элементами, но предел больше не существует в 2.11. Лимит класса дел был снят в 2.11 https://github.com/scala/scala/pull/2305
Можно было бы вручную закодировать функцию, которая преобразует списки до 22 элементов и выдает исключение для более крупных списков. Scala поддержка шаблона, предстоящая функция, сделает это более кратким. Но это было бы уродливым взломом.
Это также можно сделать в 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
).
Если вы уверены, что ваш 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)
если у вас есть тип:
val x: List[Int] = List(1, 2, 3)
def doSomething(a:Int *)
doSomething(x:_*)
2015. Для ответа Тома Крокетта, чтобы уточнить это, вот реальный пример.
Сначала я смутился. Потому что я родом из Python, где вы можете просто сделатьtuple(list(1,2,3))
. Это заставляет меня пытаться найти суть, почему Scala не может этого сделать.
В следующем примере кода реализуется метод toTuple
, который имеет тип toTupleN
и type-unsafe toTuple
.
Метод toTuple
получает информацию о типе длины во время выполнения, т.е. не имеет информации о типе длины во время компиляции, поэтому тип возврата Product
, который очень похож на Python 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)]
Используя Pattern Matching:
val intTuple = List (1,2,3) match {case List (a, b, c) => (a, b, c)}