Scalaでコードを書いていて、複数のリストをzip
することがあった。
val xs: List[Int] = ??? val ys: List[Int] = ??? val zs: List[String] = ??? xs.zip(ys).zip(zs) // => List[((Int, Int), String)]
しかしzip
は2つのリストからList[(A, B)]
を作るので、複数回zip
すると奇妙な型になってしまう。この場合はList[((Int, Int), String)]
になってしまった。こういう型になるとmap
などで使うときに面倒だ。
lazyZip
コレクションメソッドを探していると、lazyZip
メソッドというのがあることを知った(Scala 2.13から導入されたようだ)。使い方はzip
と同じだが、map
などを使うタイミングにまでタプルを作るのを遅延させてくれる。
xs.lazyZip(ys).lazyZip(zs).map { (x, y, z) => /* (Int, Int, String)のタプルになっている */ }
これは非常に便利だ。
注意点として、自動でタプルを潰してくれるのは.lazyZip
のチェインが3回を超えない場合のみだ。.lazyZip
を4度以上呼び出すと、zip
のときと同じように((A, B, C, D), E)
の形になってしまう。
zipped
ちなみに一度タプルを作成してからzipped
メソッド(Scala 2.12で登場)を呼ぶというテクニックもあるが、これはdeprecatedになっていて将来削除されるかもしれないので非推奨だ。
(xs, ys, zs).zipped // => List[(Int, Int, String)]
Catsを使う場合
関数型ライブラリであるCatsを使うと、複数のリストを柔軟にzipできるし、個数の制限がより広い。
import cats._ import cats.implicits._ val xs = List(1,2,3,4,5) val ys = List(2,3,4,5,6) val zs = List("a","b","c","d","e") (xs, ys, zs).parMapN((x, y, z) => (x, y, z)) // => List((1,2,a), (2,3,b), (3,4,c), (4,5,d), (5,6,e))
parMapN
はそれだけで記事が1つ書けるので、いったんこういうものとして覚えてほしい。
Scala 3からはタプルの個数は無制限になっているので、いくつでも動くはずだ。