web-dev-qa-db-ja.com

Scalaリストをタプルに変換しますか?

(たとえば)3つの要素を持つリストをサイズ3のタプルに変換するにはどうすればよいですか?

たとえば、val x = List(1, 2, 3)があり、これを(1, 2, 3)に変換するとします。これどうやってするの?

78
grautur

これをタイプセーフな方法で行うことはできません。どうして?一般に、実行時までリストの長さを知ることができないためです。ただし、タプルの「長さ」はそのタイプでエンコードする必要があるため、コンパイル時に認識される必要があります。例えば、 (1,'a',true)のタイプは(Int, Char, Boolean)、これはTuple3[Int, Char, Boolean]。タプルにこの制限があるのは、非同種の型を処理できる必要があるためです。

50
Tom Crockett

scalaエクストラクタとパターンマッチング( 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)

また、リストのサイズがわからない場合は、ワイルドカード演算子を使用できます

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

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メソッドに渡す必要がある理由

46
evantill

Shapeless 2. 構文を変更しました。以下は、shapelessを使用した更新されたソリューションです。

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には、リストとタプルの両方のタイプの機能があることに注意してください。つまり、タプルのようなさまざまなタイプの要素を持つことができ、標準コレクションのような他の操作間でマッピングできます。 HListは慣れるまで少し時間がかかりますので、慣れていない場合はゆっくり踏み込んでください。

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)
16
virtualirfan

単純であり、どんな長さのリストにも対応していませんが、ほとんどの場合、タイプセーフであり、答えは次のとおりです。

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
user445107

FWIW、私はフィールドの数を初期化するにタプルを望み、タプル割り当ての構文糖を使用したかった。例えば:

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

リストの内容を割り当てるための構文糖もあることがわかります...

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

同じ問題が発生した場合、タプルは不要です。

9
Peter L

これをタイプセーフな方法で行うことはできません。 Scalaでは、リストは任意の長さ何らかのタイプの要素のシーケンスです。型システムが知る限り、xは任意の長さのリストにすることができます。

対照的に、タプルのアリティはコンパイル時に知られている必要があります。型システムの安全性保証に違反して、xをTuple型に割り当てることを許可します。

実際、技術的な理由により、Scala tuples 22個の要素に制限されていました ですが、制限は2.11には存在しません。ケースクラスの制限は2.11で解除されました- https://github.com/scala/scala/pull/2305

最大22個の要素のリストを変換し、より大きなリストに対して例外をスローする関数を手動でコーディングすることができます。今後の機能であるScalaのテンプレートサポートにより、これがより簡潔になります。しかし、これはいハックになります。

6

これは、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 valshapeless.Natに変換可能)でなければなりません。

3
Kolmar

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)
2
Spark-Beginner

2015年の投稿。 Tom Crockett's answer をさらに明確化するために、ここに実際の例を示します。

最初、私はそれについて混乱しました。私はTuple(list(1,2,3))を実行できるPythonから来たからです。
それはScala言語に不足していますか?(答えは-それはScalaまたはPythonではなく、静的型と動的に関するものです-タイプ。)

だからこそ、Scalaがこれを行えない理由を見つけようとしているのです。


次のコード例では、タイプセーフtoTupleとタイプセーフでないtoTupleNを持つtoTupleメソッドを実装します。

toTupleメソッドは、実行時に型の長さ情報を取得します。つまり、コンパイル時に型の長さ情報を取得しないため、戻り型はProductになります。これはPythonのTuple実際(各位置に型はなく、型の長さもありません)。
その方法は、type-mismatchや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!
1
WeiChing Lin

あなたはこれを行うことができます

  1. パターンマッチング(不要なもの)または
  2. リストを反復処理し、各要素を1つずつ適用します。

    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
comonad

パターンマッチングの使用:

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

1

タイプがある限り:

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

def doSomething(a:Int *)

doSomething(x:_*)
1
dzs