(たとえば)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
、タプルとの間の変換を可能にするShapeless 'タイプを調べることができます(github.com/milessabin/…)。多分それはあなたのユースケースに適用できます。
回答:
タイプセーフな方法でこれを行うことはできません。どうして?一般に、実行時までリストの長さを知ることができないためです。ただし、タプルの「長さ」はその型でエンコードする必要があるため、コンパイル時に認識されます。たとえば(1,'a',true)
、タイプは(Int, Char, Boolean)
、の砂糖ですTuple3[Int, Char, Boolean]
。タプルにこの制限がある理由は、タプルが不均一なタイプを処理できる必要があるためです。
case class Vec3[A](_1: A, _2: A, _3: A)
map
機能するかを定義する必要があります
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)
}
Shapeless 2.0は、いくつかの構文を変更しました。これは、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)
単純であり、どのような長さのリストにも対応していないにもかかわらず、タイプセーフであり、ほとんどの場合、その答えは次のとおりです。
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
タプル型への割り当てを許可することは、型システムの安全保証に違反します。
実際、技術的な理由により、Scalaタプルは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についてではなく、静的型と動的型についてです。)
それが原因で、Scalaがこれを実行できない理由を見つけようとしています。
次のコード例toTuple
は、type-safetoTupleN
とtype-unsafeを持つメソッドを実装してい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!
def toTuple(x: List[Int]): R
、Rのタイプはどうあるべきですか?