Scala の Seq に定義されているメソッドを試すシリーズ(目次)。 今回はインデックスに関連するメソッドを試していきます。
今回扱うメソッド
今回扱うメソッドは以下の通り。 最初の2つ以外はインデックスの Int 値を返すメソッドです。 似たようなメソッドがありますが一貫した命名・シグニチャで定義されているので分かりやすいかと思います。def isDefinedAt(idx: Int): Boolean def indices: Range // 指定した要素のインデックスを返す def indexOf(elem: A): Int def indexOf(elem: A, from: Int): Int def lastIndexOf(elem: A): Int def lastIndexOf(elem: A, end: Int): Int // 指定した Seq が始まるインデックスを返す def indexOfSlice[B >: A](that: GenSeq[B]): Int def indexOfSlice[B >: A](that: GenSeq[B], from: Int): Int def lastIndexOfSlice[B >: A](that: GenSeq[B]): Int def lastIndexOfSlice[B >: A](that: GenSeq[B], end: Int): Int // 指定した条件に合う要素のインデックスを返す def indexWhere(p: (A) ⇒ Boolean): Int def indexWhere(p: (A) ⇒ Boolean, from: Int): Int def lastIndexWhere(p: (A) ⇒ Boolean): Int def lastIndexWhere(p: (A) ⇒ Boolean, end: Int): Int
サンプルコード
isDefinedAt メソッド
isDefinedAt メソッドは、そのインデックスに対応する要素が存在するかどうかを返します。 つまり、Seq オブジェクトの長さを length として、引数の Int 値 i が 0 <= i < length の範囲内にあれば true を、そうでなければ false を返します。 このメソッドは PartialFunction トレイトから継承しているメソッドです。val seq = Seq("a", "a", "b", "a", "b", "c") // seq.length == 6 // isDefinedAt メソッド assert( seq.isDefinedAt(4) ) assert( !seq.isDefinedAt(10) )
インデックスをチェックするための条件式が簡単に書けていいような、逆に可読性が下がるような、なんとも言えないメソッドな気が。
indices メソッド
indices*1 メソッドは、その Seq オブジェクトのインデックスを Range オブジェクト(Seq[Int] のサブ型)として返します。 まぁ、実質的に (0 until seq.length) と同じです。val seq = Seq("a", "a", "b", "a", "b", "c") // indices メソッド assert( seq.indices == (0 until seq.length) ) // 例えば for 文でこのように使える for(i <- seq.indices){ println(s"$i: ${seq(i)}") }
Seq 内の要素を走査して何かしらの処理をしたいが、インデックスの値も使いたい場合、前回やった foreach メソッドではインデックスの取得が面倒ですが、上記のサンプルコードの後半にあるように indices で for 文を回すと簡単にインデックスを扱えます(そのうちやる zipWithIndex メソッドを使ってもできますが)。
indexOf, lastIndexOf メソッド
indexOf メソッドは、指定した要素が最初に見つかった位置を返します。 第2引数に Int 値を与えると、その位置より後(その位置も含む)に見つかった要素の位置を返します。lastIndexOf メソッドは指定した要素が最後に見つかった位置を返します。 第2引数に Int 値を与えると、その位置よりも前(その位置を含む)までに見つかった要素の位置を返します。
どちらのメソッドも要素が見つからなければ -1 を返します。
val seq = Seq("a", "a", "b", "a", "b", "c") // indexOf メソッド assert( seq.indexOf("b") == 2 ) assert( seq.indexOf("b", 3) == 4) // 位置3から検索 assert( seq.indexOf("z") == -1 ) // 該当する要素がなければ -1 を返す // lastIndexOf メソッド assert( seq.lastIndexOf("b") == 4) assert( seq.lastIndexOf("b", 2) == 2 ) // 位置2まで検索(位置2の要素も含む) assert( seq.lastIndexOf("z") == -1 )
indexOfSlice, lastIndexOfSlice メソッド
indexOfSlice メソッドは、指定した Seq を部分 Seq として含む最初の位置(部分 Seq の先頭の位置)を返します。 第2引数に Int 値を指定すると、その位置から検索を開始します。lastIndexOfSlice メソッドは、指定した Seq を部分 Seq として含む最後の位置(部分 Seq の先頭の位置)を返します。 第2引数に Int 値を指定すると、その位置までの検索をします。 ただし、指定した Seq がその位置を超えて続いていても、先頭がその位置よりも前(その位置も含む)ならば検索にかかります。
どちらのメソッドでも指定した Seq が含まれていなければ -1 を返します。 また、空 Seq を指定すると先頭もしくは末尾の位置が返されます。
val seq = Seq("a", "a", "b", "a", "b", "c") // indexOfSlice メソッド val ab = Seq("a", "b") assert( seq.indexOfSlice(ab) == 1 ) assert( seq.indexOfSlice(ab, 2) == 3) // 位置2から検索を開始する val xyz = Seq("x", "y", "z") assert( seq.indexOfSlice(xyz) == -1 ) assert( seq.indexOfSlice(Seq.empty) == 0 ) // 空 Seq を渡すと先頭の位置が返される assert( seq.indexOfSlice(Seq.empty, 3) == 3 ) // 空 Seq を渡すと検索開始の位置が返される // lastIndexOfSlice メソッド assert( seq.lastIndexOfSlice(ab) == 3) assert( seq.lastIndexOfSlice(ab, 2) == 1 ) // 位置2まで検索を行う // 含まれている部分 Seq が位置2を超えていても、先頭が位置2かそれより前ならば検索にかかる assert( seq.lastIndexOfSlice(ab, 3) == 3 ) // 位置3まで検索を行う // 部分 Seq が位置3から始まっていても3が返される assert( seq.lastIndexOfSlice(xyz) == -1 ) assert( seq.lastIndexOfSlice(Seq.empty) == seq.length ) // 空 Seq を渡すとレシーバの Seq の長さが返される assert( seq.lastIndexOfSlice(Seq.empty, 3) == 3 ) // 空 Seq を渡すと検索終了位置が返される
lastIndexOfSlice メソッドは、第2引数に Int 値を指定したときに、その位置までに指定した部分 Seq が含まれていないといけないかと思ったんですが、そういう挙動ではないようですね。 もしそういうインデックスが必要なら、そのうちやる slice メソッド(take メソッドの方がいいかな?)で部分 Seq を取得してから lastIndexOfSlice メソッドを使えばいいかと思います。
indexWhere, lastIndexWhere メソッド
indexWhere メソッドは、述語関数(要素に対する条件)を引数にとり、その述語関数に適合する最初の要素の位置を返します。 第2引数に Int 値を指定すると、その位置から検索を開始します。lastIndexWhere メソッドは、述語関数を引数にとり、その述語関数に適合する最後の要素の位置を返します。 第2引数に Int 値を指定すると、その位置まで(その位置を含む)検索を行います。
述語関数に合致する要素がなければ -1 を返します。
val seq = Seq("zero", "zero", "one", "zero", "one", "two") // indexWhere メソッド assert( seq.indexWhere(_.length == 3) == 2 ) // 長さが3の要素のインデックスを返す assert( seq.indexWhere(_.length == 3, 3) == 4) // lastIndexWhere メソッド assert( seq.lastIndexWhere(_.length == 3) == 5) assert( seq.lastIndexWhere(_.length == 3, 3) == 2 )
まぁ特に問題はないかと思います。
今回はインデックスを扱うメソッドを試してきました。 インデックス関連では他に zipWithIndex メソッドというのありますが、これは zip メソッドなどとまとめてやることにします。
次回は Seq に含まれている要素のマッピングに関連するメソッドを試していく予定。

- 作者: Martin Odersky,Lex Spoon,Bill Venners,羽生田栄一,水島宏太,長尾高弘
- 出版社/メーカー: インプレス
- 発売日: 2016/09/20
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
*1:indices は index の複数形ですね。 念のため。