스칼라 목록을 튜플로 변환 하시겠습니까?


88

3 개의 요소가있는 목록을 크기 3의 튜플로 변환하려면 어떻게해야합니까?

예를 들어, 내가 가지고 val x = List(1, 2, 3)있고 이것을 (1, 2, 3). 어떻게 할 수 있습니까?


1
가능하지 않습니다 ( "손으로"제외) AFAIK. 주어진 def toTuple(x: List[Int]): RR의 유형은 무엇입니까?

7
임의의 크기의 튜플에 대해 수행 할 필요가없는 경우 (즉, 위에 제시된 가상의 방법 x match { case a :: b :: c :: Nil => (a, b, c); case _ => (0, 0, 0) }과 같이) 결과 유형이 다음과 같이 고정되어 있음을 고려 하고 유의하십시오.Tuple3[Int,Int,Int]

2
으로 할 수있는 작업은 불가능하지만 튜플 ( github.com/milessabin/… ) 과의 변환을 허용하는 ShapelessList ' HList유형을 살펴볼 수 있습니다 . 사용 사례에 적용 할 수 있습니다.

답변:


59

형식이 안전한 방식으로는이 작업을 수행 할 수 없습니다. 왜? 일반적으로 우리는 런타임까지 목록의 길이를 알 수 없기 때문입니다. 그러나 튜플의 "길이"는 해당 유형으로 인코딩되어야하며 따라서 컴파일 타임에 알려 져야합니다. 예를 들면(1,'a',true) 에는 유형 (Int, Char, Boolean)Tuple3[Int, Char, Boolean]있습니다. 튜플에이 제한이있는 이유는 비 동종 유형을 처리 할 수 ​​있어야하기 때문입니다.


동일한 유형의 고정 크기 요소 목록이 있습니까? 물론 형태가없는 것과 같은 비표준 라이브러리를 가져 오지 않습니다.

1
내가 알고있는 것을하지 @davips하지만, 자신의, 예를 들어 쉽게 만들 충분case class Vec3[A](_1: A, _2: A, _3: A)
톰 크로켓

이것은 동일한 유형을 가진 요소의 "튜플"이 될 것입니다. 예를 들어지도를 적용 할 수 없습니다.

1
새로운 데이터와 같은 @davips는 어떻게 정의해야 할 것 입력 map이 작동 등
톰 크로켓을

1
이러한 종류의 제한은 무엇보다 형식 안전의 무용 함을 나타내는 확연한 지표 인 것 같습니다. "빈 세트에는 많은 흥미로운 속성이 있습니다."라는 인용구가 떠 오릅니다.
ely

58

스칼라 추출기와 패턴 일치를 사용하여 수행 할 수 있습니다 ( link ).

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)

또한 List의 크기가 확실하지 않은 경우 와일드 카드 연산자를 사용할 수 있습니다.

val t = x match {
  case List(a, b, c, _*) => (a, b, c)
}

4
이 솔루션의 맥락에서 유형 안전성에 대한 불만은 런타임에 MatchError가 발생할 수 있음을 의미합니다. 원하는 경우 해당 오류를 시도하고 목록의 길이가 잘못된 경우 작업을 수행 할 수 있습니다. 이 솔루션의 또 다른 멋진 기능은 출력이 튜플 일 필요가 없으며 사용자 정의 클래스 중 하나이거나 숫자의 일부 변환 일 수 있다는 것입니다. 나는 당신이 비 Lists로 이것을 할 수 있다고 생각하지 않습니다 (따라서 입력에 .toList 작업을 수행해야 할 수도 있습니다).
Jim Pivarski 2014 년

+ : 구문을 사용하여 모든 유형의 Scala 시퀀스로이를 수행 할 수 있습니다. 또한, 포괄 추가하는 것을 잊지 마세요
IG-DEV

48

형태없는 사용 예 :

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의 List를 변환하기 위해 몇 가지 유형 정보가 필요하므로 유형 정보를 toHList메서드에 전달해야하는 이유


19

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 유형의 기능이 있습니다. 즉, 튜플과 같은 다른 유형의 요소를 가질 수 있으며 표준 컬렉션과 같은 다른 작업간에 매핑 될 수 있습니다. 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)

13

단순하고 길이가없는 목록에도 불구하고 형식에 안전하며 대부분의 경우에 대한 대답입니다.

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

10

FWIW에서는 튜플 이 여러 필드초기화하고 튜플 할당의 구문 설탕을 사용하기를 원했습니다. 예 :

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

목록의 내용을 할당하기위한 구문 설탕도 있다는 것이 밝혀졌습니다.

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

따라서 동일한 문제가 발생하면 튜플이 필요하지 않습니다.


@javadba listToTuple ()을 구현하는 방법을 찾고 있었지만 그럴 필요가 없었습니다. 목록 구문 설탕이 내 문제를 해결했습니다.
Peter L

내 의견으로는 좀 더 좋아 보이는 또 다른 구문도 있습니다.val List(c1, c2, c3) = myList
Kolmar

7

형식이 안전한 방식으로는이 작업을 수행 할 수 없습니다. Scala에서 목록은 어떤 유형의 요소의 임의 길이 시퀀스입니다. 유형 시스템이 아는 한x 한 임의의 길이 목록이 될 수 있습니다.

반대로, 튜플의 배열은 컴파일 타임에 알아야합니다. 할당을 허용하는 유형 시스템의 안전 보장을 위반합니다.x튜플 유형에 을 .

사실 기술적 인 이유로 스칼라 튜플 은 22 개 요소로 제한되었지만 2.11에서는 더 이상 존재하지 않습니다. 케이스 클래스 제한은 2.11에서 해제되었습니다. https://github.com/scala/scala/pull/2305

최대 22 개 요소의 목록을 변환하고 더 큰 목록에 대해 예외를 발생시키는 함수를 수동으로 코딩 할 수 있습니다. 곧 출시 될 기능인 Scala의 템플릿 지원은이를 더 간결하게 만들 것입니다. 그러나 이것은 추악한 해킹이 될 것입니다.


이 가상 함수의 결과 유형이 Any가 될까요?

케이스 클래스 제한은 2.11 github.com/scala/scala/pull/2305
gdoubleod

5

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)

5

다음을 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) 이어야합니다 .


2.3.3 :이 적어도 shapeless_2.11를 들어, 22보다 큰 크기에 대한 작업에 표시되지 않습니다
kfkhalili

@kfkhalili 예, 형태가없는 제한이 아닙니다. Scala 2 자체는 22 개 이상의 요소가있는 튜플을 허용하지 않으므로 어떤 방법을 사용하여 더 큰 크기의 List를 Tuple로 변환 할 수 없습니다.
Kolmar

4

패턴 매칭 사용 :

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


이 오래된 (6 년 이상) 질문에 답변 할 때 귀하의 답변이 이미 제출 된 모든 (12) 이전 답변과 어떻게 다른지 지적하는 것이 좋습니다. 특히, 귀하의 대답은 List/ 의 길이 Tuple가 알려진 상수 인 경우에만 적용됩니다 .
jwvh

@ jwvh 감사합니다. 앞으로 귀하의 의견을 고려할 것입니다.
Michael Olafisoye

2

2015 년 포스트. 를 들어 톰 크로켓의 대답은 더 할 수 명확히 , 여기에 실제 예입니다.

처음에는 혼란 스러웠습니다. 저는 Python에서 왔기 때문에 tuple(list(1,2,3)).
Scala 언어가 부족합니까? (답은-Scala 나 Python이 아니라 정적 유형과 동적 유형에 관한 것입니다.)

그것이 내가 왜 스칼라가 이것을 할 수 없는지 핵심을 찾으려고하는 이유이다.


다음 코드 예제 toTuple에서는 type-safe toTupleN및 type-unsafe 가 있는 메서드 를 구현합니다 toTuple.

toTuple메서드는 런타임에 유형 길이 정보를 얻습니다. 즉, 컴파일 시간에 유형 길이 정보가 없으므로 반환 유형은 실제로 ProductPython과 매우 유사합니다 tuple(각 위치에 유형이없고 유형 길이가 없음).
그런 식으로 유형 불일치 또는IndexOutOfBoundException . (따라서 Python의 편리한 list-to-tuple은 공짜 점심이 아닙니다.)

반대로 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!

외모의 좋은 - 지금은 그것을 밖으로 시도
StephenBoesch

2

당신은 이것을 할 수 있습니다

  1. 패턴 매칭 (원하지 않는 것) 또는
  2. 목록을 반복하고 각 요소를 하나씩 적용합니다.

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

1

유형이있는 한 :

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

def doSomething(a:Int *)

doSomething(x:_*)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.