web-dev-qa-db-ja.com

複数のシーケンスを圧縮

私は長いタプルを形成するために複数のシーケンスをZipしようとしています:

val ints = List(1,2,3)
val chars = List('a', 'b', 'c')
val strings = List("Alpha", "Beta", "Gamma")
val bools = List(true, false, false)

ints Zip chars Zip strings Zip bools

私が得るもの:

List[(((Int, Char), String), Boolean)] =
  List((((1,a),Alpha),true), (((2,b),Beta),false), (((3,c),Gamma),false))

しかし、私はflatタプルのシーケンスを取得したいと思います:

List[(Int, Char, String, Boolean)] = 
  List((1,a,Alpha,true), (2,b,Beta,false), (3,c,Gamma,false))

私は今できます:

List(ints, chars, strings, bools).transpose

しかし、弱い型付けのList[List[Any]]を返します。また、(ints, chars, strings).zippedを実行できますが、zippedは2タプルと3タプルでのみ機能します。

(任意の)数の等長シーケンスを簡単にZipする方法はありますか?

34

ここにあなたの例を解決する一つの方法がありますが、これは任意の数のシーケンスのためではありません。

val ints = List(1,2,3)
val chars = List('a', 'b', 'c')
val strings = List("Alpha", "Beta", "Gamma")
val bools = List(true, false, false)

val input = ints Zip chars Zip strings Zip bools

// Flattens a Tuple ((A,B),C) into (A,B,C)
def f2[A,B,C](t: ((A,B),C)) = (t._1._1, t._1._2, t._2)

// Flattens a Tuple ((A,B),C,D) into (A,B,C,D)
def f3[A,B,C,D](t: ((A,B),C,D)) = (t._1._1, t._1._2, t._2, t._3)

input map f2 map f3

少なくともこの種のソリューションでは、一般的に任意の長さのタプルに対してそれを行うことは不可能だと思います。タプルは強く型付けされており、私が知る限り、型システムでは型パラメーターの可変数を指定できません。これにより、任意の長さのタプルを取るf2およびf3の一般化バージョンを作成することができなくなります。 ((A,B),C,D,...)(タプル(A,B,C,D,...)を返します)。

可変数の型パラメーターを指定する方法があった場合、Scalaの標準ライブラリにあるTuple1Tuple2、... Tuple22の特性は必要ありません。

11
Jesper

データセットを表すクラスを作成します。

case class DataSet(int: Int, char: Char, string: String, bool: Boolean)

これにより、タプル内にある_Nの代わりに、値にアクセスするためのより適切な名前が提供されます。リストのサイズが異なる場合は、最も短いものを選択する必要があります。

val min = List(ints, chars, strings, bools).map(_.size).min

これで、データを抽出することができます。

val dataSets = (0 until min) map { i => DataSet(ints(i), chars(i), strings(i), bools(i)) }

元のリストに多くの値を含めることができる場合は、それらをIndexedSeqにして、アクセス時間がO(1)になるようにすることをお勧めします。

7
kiritsuku

パターンマッチングは良いオプションだと思います

val ints = List(1,2,3)
val chars = List('a', 'b', 'c')
val strings = List("Alpha", "Beta", "Gamma")
val bools = List(true, false, false)
(ints Zip chars Zip strings Zip bools) map { case (((i,c),s),b) => (i,c,s,b)}

**res1: List[(Int, Char, Java.lang.String, Boolean)] = List((1,a,Alpha,true), (2,b,Beta,false), (3,c,Gamma,false))**

または、タイプも追加できます

(ints Zip chars Zip strings Zip bools) map {case (((i:Int,c:Char),s:String),b:Boolean) => (i,c,s,b)}

**res2: List[(Int, Char, Java.lang.String, Boolean)] = List((1,a,Alpha,true), (2,b,Beta,false), (3,c,Gamma,false))**
7
igx

shapeless を使用すると、次のことができます。

import shapeless.Tuples._

val ints = (1, 2, 3)
val chars = ('a', 'b', 'c')

val megatuple = (ints, chars)

val megahlist = (megatuple hlisted) map hlisted

val transposed = (mhlist transpose) map tupled tupled

scala> transposed
res: ((Int, Char), (Int, Char), (Int, Char)) = ((1,a),(2,b),(3,c))

(確かではありませんが、mapと前後の変換を回避できるように定義されたインプリクトがさらにある場合)

[編集:この部分はもう当てはまりません。

無形のドキュメントによると、変換はTuple4は現在サポートされています。その場合は、HListを手動で作成する必要があります。]

5
Debilski

各タプルアリティはソースコードで個別のクラスとして表されるため、コードジェネレーターを使用しない限り、それらにアクセスするために個別のコードを記述する必要があるため、これは一般的に不可能であるというJesperの意見を共有します。

しかし、私は別の可能な解決策を追加したいと思います。タプルエントリの型を保持したいが、コレクションのような型に興味がある場合は、HList(異種リスト)が適しています。 google hlist scala 実装と説明。

2
ziggystar

ここにあなたの問題に役立つ別の解決策があります。

ints Zip chars Zip strings Zip bools map{ case (((a, b), c), d) => (a,b,c,d)}
1
Shirish Namdeo

product-collections の使用

scala> ints flatZip chars flatZip strings flatZip bools
res0: org.catch22.collections.immutable.CollSeq4[Int,Char,String,Boolean] = 
CollSeq((1,a,Alpha,true),
        (2,b,Beta,false),
        (3,c,Gamma,false))

これは現在アリティ1〜22で機能します。ご覧のとおり、タイプは保持されています。

1
Mark Lister