Преобразовать список Scala в кортеж?

Как я могу преобразовать список с (скажем) тремя элементами в кортеж размером 3?

Например, допустим, что у меня есть val x = List(1, 2, 3), и я хочу преобразовать его в (1, 2, 3). Как я могу это сделать?

Ответ 1

Вы не можете сделать это по-разному. Зачем? Потому что в общем случае мы не можем знать длину списка до времени выполнения. Но "длина" кортежа должна быть закодирована в своем типе и, следовательно, известна во время компиляции. Например, (1,'a',true) имеет тип (Int, Char, Boolean), который является сахаром для Tuple3[Int, Char, Boolean]. Причина, по которой кортежи имеют это ограничение, заключается в том, что они должны иметь возможность обрабатывать неоднородные типы.

Ответ 2

Вы можете сделать это, используя 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)
}

Ответ 3

пример с использованием 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

Ответ 4

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)

Ответ 5

FWIW, мне нужен кортеж для инициализации нескольких полей и хотел использовать синтаксический сахар для назначения кортежа. EG:

val (c1, c2, c3) = listToTuple(myList)

Оказывается, есть синтаксический сахар для назначения содержимого списка тоже...

val c1 :: c2 :: c3 :: Nil = myList

Так что нет необходимости в кортежах, если у вас такая же проблема.

Ответ 6

Несмотря на простоту и не для списков любой длины, он является типобезопасным и ответ в большинстве случаев:

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

Ответ 7

Вы не можете сделать это безопасным способом. В Scala перечислены произвольные последовательности элементов некоторого типа. Насколько известно системе типов, x может быть списком произвольной длины.

Напротив, арность кортежа должна быть известна во время компиляции. Это нарушит гарантии безопасности системы типов, позволяющие присваивать x типу кортежа.

Фактически, по техническим причинам Scala tuples были ограничены 22 элементами, но предел больше не существует в 2.11. Лимит класса дел был снят в 2.11 https://github.com/scala/scala/pull/2305

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

Ответ 8

Это также можно сделать в 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).

Ответ 9

Если вы уверены, что ваш 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)

Ответ 10

если у вас есть тип:

val x: List[Int] = List(1, 2, 3)

def doSomething(a:Int *)

doSomething(x:_*)

Ответ 11

2015. Для ответа Тома Крокетта, чтобы уточнить это, вот реальный пример.

Сначала я смутился. Потому что я родом из Python, где вы можете просто сделать tuple(list(1,2,3)).
Не хватает ли языка Scala? (ответ - это не о Scala или Python, это о статическом и динамическом типах.)

Это заставляет меня пытаться найти суть, почему 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!

Ответ 12

вы можете сделать это либо

  • с помощью сопоставления с образцом (что вам не нужно) или
  • путем повторения списка и применения каждого элемента по одному.

    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)]
    

Ответ 13

Используя Pattern Matching:

val intTuple = List (1,2,3) match {case List (a, b, c) => (a, b, c)}