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


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.