3 개의 요소가있는 목록을 크기 3의 튜플로 변환하려면 어떻게해야합니까?
예를 들어, 내가 가지고 val x = List(1, 2, 3)
있고 이것을 (1, 2, 3)
. 어떻게 할 수 있습니까?
3 개의 요소가있는 목록을 크기 3의 튜플로 변환하려면 어떻게해야합니까?
예를 들어, 내가 가지고 val x = List(1, 2, 3)
있고 이것을 (1, 2, 3)
. 어떻게 할 수 있습니까?
x match { case a :: b :: c :: Nil => (a, b, c); case _ => (0, 0, 0) }
과 같이) 결과 유형이 다음과 같이 고정되어 있음을 고려 하고 유의하십시오.Tuple3[Int,Int,Int]
List
' HList
유형을 살펴볼 수 있습니다 . 사용 사례에 적용 할 수 있습니다.
답변:
형식이 안전한 방식으로는이 작업을 수행 할 수 없습니다. 왜? 일반적으로 우리는 런타임까지 목록의 길이를 알 수 없기 때문입니다. 그러나 튜플의 "길이"는 해당 유형으로 인코딩되어야하며 따라서 컴파일 타임에 알려 져야합니다. 예를 들면(1,'a',true)
에는 유형 (Int, Char, Boolean)
이 Tuple3[Int, Char, Boolean]
있습니다. 튜플에이 제한이있는 이유는 비 동종 유형을 처리 할 수 있어야하기 때문입니다.
case class Vec3[A](_1: A, _2: A, _3: A)
map
이 작동 등
스칼라 추출기와 패턴 일치를 사용하여 수행 할 수 있습니다 ( 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)
}
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)
단순하고 길이가없는 목록에도 불구하고 형식에 안전하며 대부분의 경우에 대한 대답입니다.
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
튜플 유형에 을 .
사실 기술적 인 이유로 스칼라 튜플 은 22 개 요소로 제한되었지만 2.11에서는 더 이상 존재하지 않습니다. 케이스 클래스 제한은 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) match {case List (a, b, c) => (a, b, c)}
List
/ 의 길이 Tuple
가 알려진 상수 인 경우에만 적용됩니다 .
2015 년 포스트. 를 들어 톰 크로켓의 대답은 더 할 수 명확히 , 여기에 실제 예입니다.
처음에는 혼란 스러웠습니다. 저는 Python에서 왔기 때문에 tuple(list(1,2,3))
.
Scala 언어가 부족합니까? (답은-Scala 나 Python이 아니라 정적 유형과 동적 유형에 관한 것입니다.)
그것이 내가 왜 스칼라가 이것을 할 수 없는지 핵심을 찾으려고하는 이유이다.
다음 코드 예제 toTuple
에서는 type-safe toTupleN
및 type-unsafe 가 있는 메서드 를 구현합니다 toTuple
.
이 toTuple
메서드는 런타임에 유형 길이 정보를 얻습니다. 즉, 컴파일 시간에 유형 길이 정보가 없으므로 반환 유형은 실제로 Product
Python과 매우 유사합니다 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!
def toTuple(x: List[Int]): R
R의 유형은 무엇입니까?