33
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

第7章:タプルにチャレンジ!

Posted at

第7章も相変わらず について語るぞ!
今回の主役は、Javaプログラマーには馴染みがない タプル

複数の戻り値

戻り値で複数の異なる型を返したくなったことはある?
Javaだと引数の型は1つと決まっているから、単純には実現できないよね。
だから、代替案としてこんな手を使ったりするよね。

  • 例えば、新たなDTO等の 情報格納役 を定義し、そのフィールドに複数の型を用意する
  • 引数に複数のオブジェクトを渡して、戻り値相当をそのオブジェクトに設定する
  • インタフェースを分割する。つまりそれぞれのデータを返すメソッドを用意する
  • 戻り値をコレクションにして、複数の型のオブジェクトを格納する(ほとんどやらないけど)

どれも面倒臭いんだよね。クラス名決めるのだって大変だし。。。

Javaの世界では諦めモードだけど、Scalaの世界では素晴らしい仕組みが備わっているんだ。
複数の型を返す仕組み、 タプル だ!

タプルの例

早速ソースを見てみよう。

Tuple.scala
object Tuple {

  def getMaxValue(numbers: List[Int]) = {
    val max = numbers.max
    val index = numbers.indexOf(max)
    (max, index)
  }

  def main(args: Array[String]){
    val numbers = List(1, 2, 3, 4, 5, 10, 6)

    val maxValue = getMaxValue(numbers)
    printf("max=%s \n", maxValue._1)
    printf("index=%s \n", maxValue._2)
  }

}

実行

実行すると以下のようになるよ。

$ scalac Tuple.scala

$ scala Tuple
max=10
index=5

ソースの説明

Tuple.scalaは単純なソースだ。
引数にIntListを受け取り、以下の戻り値を返すメソッドgetMaxValueを定義する。

  • 引数のList最大値
  • 引数のList最大値のインデックス

そして、その戻り値を確認する。

この説明だけで、メソッドgetMaxValue内でどれがタプルかわかるだろ?
(max, index)がタプルだ。

val max = numbers.max
val index = numbers.indexOf(max)

Listの最大値と最大値のインデックスはここで取得しているんだ。

val maxValue = getMaxValue(numbers)
printf("max=%s \n", maxValue._1)
printf("index=%s \n", maxValue._2)

タプルを変数maxValueに格納して、_1 _2でそれぞれの要素にアクセスしている。
格納した順番通りなので

  • _1が最大値
  • _2が最大値のインデックス

となる。

タプルとは

タプルとは、 複数の異なる型 を格納できるコンテナオブジェクトだ。
つまり 情報格納役 になる。

もちろんタプルにも型が存在している。
scala.TupleNとして、1~22のNに対してTupleNクラスが定義されている(ようだ)。
ということは、タプルには22個まで要素を格納できる。

また、それぞれの要素にアクセスするには_Nとする。

タプルの限界

22個なんて使うことはないと思いつつ、22個を超過したらどうなるか試してみる。

Tuple22

22個は最大値なので問題ないはず。

Tuple22.scala
object Tuple22 {

  def get() = {
    (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22)
  }

  def main(args: Array[String]){
    val tuple = get()
    printf("element_1=%s \n", tuple._1)
    printf("element_22=%s \n", tuple._22)
  }

}

22個の要素を持つタプルを用意して、1要素目と22要素目を取得する。

実行

$ scalac Tuple22.scala

$ scala Tuple22
element_1=1
element_22=22

これは期待通りの動きとなった。

Tuple23

23個の要素を持つタプルは、型が存在しないのでscalacでエラーになるはず。

Tuple23.scala
object Tuple23 {

  def get() = {
    (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23)
  }

  def main(args: Array[String]){
    val tuple = get()
    printf("element_1=%s \n", tuple._1)
    printf("element_23=%s \n", tuple._23)
  }

}

実行

scalacしてみると

$ scalac Tuple23.scala
Tuple23.scala:4: error: object Tuple23 is not a member of package scala
      (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23)
      ^
one error found

Tuple23なんていないぜって言っているから、予想通りだ。
23個以上は使うなよ!

Function22

Function22というのもあるんだ。
引数の数に応じて、Function1Function22まで存在している。
つまり23個以上は使えないってこと?

Func22.scala
object Func22 {
  def main(args: Array[String]) {
    val func22 : (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int = null;
  }
}

実行

scalacしてみるよ!

scalac Func22.scala

コンパイルエラーはでないね。

Function23

次はFunction23を見てみよう。

Func23.scala
object Func23 {
  def main(args: Array[String]) {
    val func23 : (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int = null;
  }
}

実行

scalacしてみるよ!

$ scalac Func23.scala
Func23.scala:3: error: type Function23 is not a member of package scala
    val func23 : (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int = null;
                                                 ^
one error found

今度はコンパイルエラーになったね。Function23なんて型はないぜ!って言っている。
関数の引数は、やっぱり22個までということですね。

情報格納役オブジェクトで試す

Intの場合で試してみたので、今度は情報格納役オブジェクト
所謂JavaBean的なオブジェクトで試してみよう!

振る舞いも入れてみてるからな。

CaseClassTuple.scala
object CaseClassTuple {

  def get() = {
    val name = new Name("清美", "椿山")
    (Human(name, 15), name)
  }

  def main(args: Array[String]){
    val tuple = get()
    printf("fullname_1=%s \n", tuple._1.name.fullname)
    printf("fullname_2=%s \n", tuple._2.fullname)
  }

}

class Name(first: String, last: String) {
  def fullname() = first + " " + last
}

case class Human(name: Name, age: Int)

実行

実行してみる。

$ scalac CaseClassTuple.scala

$ scala CaseClassTuple
fullname_1=清美 椿山
fullname_2=清美 椿山

これもウマくいった。
ほぼほぼどんなオブジェクトでもいけそうな予感。

タプルもイミュータブル

もちろんというか、タプルもイミュータブルなので要素の入れ替えができないよ。
ちょっとチャレンジしてみました。

TupleImmutable.scala
object TupleImmutable {

  def get(x1: Int, x2: Int) = {
    (x1, x2)
  }

  def main(args: Array[String]){
    val tuple = get(1, 2)
    tuple._1 = 3
  }

}

実行

scalacしてみる。

$ scalac TupleImmutable.scala
TupleImmutable.scala:9: error: reassignment to val
tuple._1 = 3
         ^
one error found

__イミュータブルだぜ!アサインできないぞ!__と言っているね!

タプルのメリット

型の増大を防ぐことができるのがメリットだと思うよ。
特に、サービス層で使える気がしている。

異なる型のデータ取得のためだけに、サービスのインタフェースを複数切っていたところを、
Facadeにまとめてしまい、タプルでデータを返すってコトができると思う。

他にも使い道ありそうだね。

まとめ

今回はタプルについて語ってみたけどどうかな?
自分が疑問に思っていたところも試すことが出来たから、良かったぞ。

今回も
体で感じてくれたかな?

33
25
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
33
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?