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


88

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

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


1
(「手作業」を除いて)AFAIKは不可能です。とするとdef toTuple(x: List[Int]): R、Rのタイプはどうあるべきですか?

7
任意のサイズのタプルに対して実行する必要がない場合(つまり、上記の架空の方法のように)、x match { case a :: b :: c :: Nil => (a, b, c); case _ => (0, 0, 0) }結果の型がTuple3[Int,Int,Int]

2
を使用して実行しようとしていることは不可能ListですがHList、タプルとの間の変換を可能にするShapeless 'タイプを調べることができます(github.com/milessabin/…)。多分それはあなたのユースケースに適用できます。

回答:


59

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


同じタイプの要素の固定サイズのリストはありますか?もちろん、シェイプレスなどの非標準ライブラリはインポートしません。

1
@davipsは私が知っていることではありませんが、自分で作成するのは簡単です。例case class Vec3[A](_1: A, _2: A, _3: A)
Tom Crockett

これは同じタイプの要素の「タプル」になります。たとえば、マップを適用することはできません。

1
@davipsは、他の新しいデータ型と同様に、そのためにどのようにmap機能するかを定義する必要があります
Tom Crockett

1
この種の制限は、何よりも型安全性の無用さをはっきりと示しているようです。「空のセットには多くの興味深い特性がある」という引用を思い出します。
ely 2018

58

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

4
このソリューションのコンテキストでは、型安全性に関する苦情は、実行時にMatchErrorが発生する可能性があることを意味します。必要に応じて、そのエラーをキャッチして、リストの長さが間違っている場合に何かを行うことができます。このソリューションのもう1つの優れた機能は、出力がタプルである必要はなく、独自のカスタムクラスの1つにすることも、数値を変換することもできることです。ただし、非リストではこれを実行できないと思います(したがって、入力に対して.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内のリストを変換するためにいくつかの型情報を必要とします。これは、型情報をtoHListメソッドに渡す必要がある理由です。


18

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)

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
Kolmar19年

7

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

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

実際、技術的な理由により、Scalaタプルは22要素に制限されていましたが、2.11では制限がなくなりました。2.11ではケースクラスの制限が解除されましたhttps://github.com/scala/scala/pull/2305

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


この仮想関数の結果のタイプはAnyでしょうか?

ケースクラスの制限は2.11で解除されましたgithub.com/scala/scala/pull/2305– gdoubleod 2016
1

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


これは、少なくともshapeless_2.11のために、22よりも大きいサイズのために仕事には表示されません:2.3.3
kfkhalili

@kfkhaliliはい、それは形のない制限ではありません。Scala 2自体は、22を超える要素を持つタプルを許可しないため、どの方法を使用しても、より大きなサイズのリストをタプルに変換することはできません。
コルマー

4

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

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


この古い(6年以上)質問に答えるとき、あなたの答えがすでに提出されたすべての(12)古い答えとどのように異なるかを指摘することはしばしば良い考えです。特に、あなたの答えは、List/の長さがTuple既知の定数である場合にのみ適用できます。
jwvh

ありがとう@jwvh、今後のコメントを検討します。
MichaelOlafisoye19年

2

2015年の投稿。以下のためにトム・クロケットの答えは 多くのことが明らかに、ここでは実際の例です。

最初、私はそれについて混乱しました。私はPythonから来たので、あなたはただそれを行うことができますtuple(list(1,2,3))
Scala言語が不足していますか?(答えは-それはScalaやPythonについてではなく、静的型と動的型についてです。)

それが原因で、Scalaがこれを実行できない理由を見つけようとしています。


次のコード例toTupleは、type-safetoTupleNとtype-unsafeを持つメソッドを実装していtoTupleます。

ザ・ toTupleメソッドは、実行時に型長情報を取得します。つまり、コンパイル時に型長情報を取得しないため、戻り値の型はProductPythonの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!

素敵なルックス-今それを試してみる
javadba

2

あなたはどちらかを行うことができます

  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

あなたがタイプを持っている限り:

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

def doSomething(a:Int *)

doSomething(x:_*)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.