Scala の Seq に定義されているメソッドを試すシリーズ(目次)。 前回はコンパニオン・オブジェクトに定義されているファクトリ・メソッドを試しましたが、今回からは Seq トレイトに定義されているメソッドを見ていきます。 一度に全てを見るのは無理なので適当に分類しています。 今回は Seq 内の要素の有無と要素数に関連するメソッドです。
今回扱うメソッド
今回扱うメソッドの定義を見ておきましょう。 まずは Seq 内の要素の有無に関するメソッド。 Boolean 値を返すメソッドを集めています:// 要素の有無に関するメソッド def isEmpty: Boolean def nonEmpty: Boolean def contains[A1 >: A](elem: A1): Boolean def containsSlice[B](that: GenSeq[B]): Boolean def startsWith[B](that: GenSeq[B]): Boolean def startsWith[B](that: GenSeq[B], offset: Int): Boolean def endsWith[B](that: GenSeq[B]): Boolean def sameElements(that: GenIterable[A]): Boolean def forall(p: (A) ⇒ Boolean): Boolean def exists(p: (A) ⇒ Boolean): Boolean def corresponds[B](that: GenSeq[B])(p: (A, B) ⇒ Boolean): Boolean
もう一つは Seq の要素の個数に関するメソッド。 主に要素数に関する Int 値を返すメソッドを集めています。 hasDefiniteSize メソッドは Boolean 値を返しますが。
// 要素の個数に関するメソッド def hasDefiniteSize: Boolean def length: Int def size: Int def lengthCompare(len: Int): Int def count(p: (A) ⇒ Boolean): Int def prefixLength(p: (A) ⇒ Boolean): Int def segmentLength(p: (A) ⇒ Boolean, from: Int): Int
では1つ1つメソッドを試していきましょう。
サンプルコード(要素の有無)
まずは要素の有無に関するメソッド。isEmpty メソッド
isEmpty メソッドは、その Seq オブジェクトが空かどうかを返します。 空の場合は true、空でない場合は false を返します。val emp = Seq.empty[Int] val seq = Seq(0, 1, 2, 3, 4) // isEmpty メソッド assert( emp.isEmpty ) assert( !seq.isEmpty ) // seq0.nonEmpty の方が望ましい
空でないことを確かめたい(空でないときに true を返す)場合には、次の nonEmpty メソッドを使う方が良いようです。
nonEmpty メソッド
nonEmpty メソッドは、その Seq オブジェクトが空でないかどうかを返します。 空でない場合は true、空の場合は false を返します。val emp = Seq.empty[Int] val seq = Seq(0, 1, 2, 3, 4) // nonEmpty メソッド assert( !emp.nonEmpty ) // empty の方が望ましい assert( seq.nonEmpty )
contains メソッド
contains メソッドは、その Seq オブジェクトが引数で指定した要素を含んでいるかどうかを返します。val seq = Seq(0, 1, 2, 3, 4) // contains メソッド assert( seq.contains(1) ) assert( !seq.contains(5) )
containsSlice メソッド
containsSlice メソッドは、その Seq オブジェクトが指定した Seq をサブ Seq (部分 Seq)を含んでいるかどうかを返します。val seq = Seq(0, 1, 2, 3, 4) // containsSlice メソッド assert( seq.containsSlice(Seq(1, 2, 3)) ) assert( !seq.containsSlice(Seq(1, 5)) ) // 「5」を含んでないのでダメ assert( !seq.containsSlice(Seq(1, 3)) ) // 「1, 3」という連なりで含んでいないのでダメ assert( seq.containsSlice(Nil) ) // 空 Seq は全ての Seq に含まれる
サブ Seq として含まれているかどうかなので、順序や連なりも合っていないといけません。 また、空 Seq を引数として渡すと true が返されます。
startsWith, endsWith メソッド
startsWith, endsWith メソッドは、その Seq オブジェクトがそれぞれ指定された Seq で始まる、もしくは終わるかどうかを返します。 startsWith メソッドで2つの引数をとるものは、2つ目の引数でサブ Seq を調べ始める位置を指定します。val seq = Seq(0, 1, 2, 3, 4) // startsWith メソッド assert( seq.startsWith(Seq(0, 1)) ) assert( !seq.startsWith(Seq(2, 3)) ) assert( seq.startsWith(Seq(2, 3), 2) ) // seq の位置2以降の Seq、つまり Seq(2, 3, 4) に対して startsWith assert( seq.startsWith(Nil) ) // 空 Seq を渡すと true を返す assert( seq.startsWith(Nil, 2) ) // endsWith メソッド assert( seq.endsWith(Seq(2, 3, 4)) ) assert( seq.endsWith(Nil) ) // 空 Seq を渡すと true を返す
contains と同じように、引数として空 Seq を渡した場合は true を返します。
sameElementsメソッド
sameElments メソッドは、その Seq オブジェクトが指定された Seq と同じ要素を同じ順序で含んでいるかどうかを返します。 Seq オブジェクトに対する「==」演算子と同じ等価性評価なので、「==」を使う方がいいようです。val seq = Seq(0, 1, 2, 3, 4) // sameElements メソッド assert( seq.sameElements(0 to 4) ) // 実装クラスが違っても大丈夫 assert( !seq.sameElements(Seq(4, 3, 2, 1, 0)) ) assert( seq == (0 to 4) ) // 「==」と同じ
forall メソッド
forall メソッドは、要素に対する述語関数(要素を受け取りBoolean 値を返す関数)を引数にとって、全ての要素がその述語関数を満たすかどうかを返します。 各要素に対する述語関数の結果を論理積 AND で合わせたものですね。val seq = Seq(0, 1, 2, 3, 4) val emp = Seq.empty[Int] // forall メソッド assert( seq.forall(_ < 10) ) // 全ての要素が10未満 ⇒ true assert( !seq.forall(_ < 3) ) // 全ての要素が3未満 ⇒ false assert( emp.forall(_ => false) ) // 空 Seq に対しては常に true
空 Seq に対して呼び出すと true を返します。
exists メソッド
exists メソッドは、要素に対する述語関数を引数にとって、ある要素がその述語関数を満たすかどうかを返します。 こちらは各要素に対する述語関数の結果を論理和 OR で合わせたものですね。val seq = Seq(0, 1, 2, 3, 4) val emp = Seq.empty[Int] // exists メソッド assert( seq.exists(_ < 1) ) // 1未満の要素が存在する ⇒ true assert( !seq.exists(_ > 10) ) // 10より大きい要素が存在する ⇒ false assert( !emp.exists(_ => true) ) // 空 Seq に対しては常に false
空 Seq に対して呼び出すと false を返します。 そんな要素は存在しないので。
corresponds メソッド
corresponds メソッドは2つの Seq オブジェクトが何かしらの対応関係があるかどうかを返します。 対応関係は2つ目の引数リストで指定します。val seq = Seq(0, 1, 2, 3, 4) val seq1 = Seq(0, 2, 4, 6, 8) // corresponds メソッド // ちょっと制御構造風に書いてみた assert( (seq corresponds seq1){ (i, j) => i*2 == j // seq の要素を2倍すると、対応する seq1 の要素になるかどうか } ) val isHalfOf: (Int, Int) => Boolean = (i, j) => i*2 == j assert( !(seq corresponds Seq(0, 1, 4, 6, 7))(isHalfOf) ) // 「7」がダメ assert( !(seq corresponds Seq(0, 1, 4, 6))(isHalfOf) ) // 要素数が違うとダメ // 2つの Seq オブジェクトの要素型が一致する必要はない val seq2 = Seq("0", "1", "2", "3", "4") // Seq[String] assert( (seq corresponds seq2)((i, s) => i.toString == s) )
比べる2つの Seq オブジェクトは、要素数が一致していないと false を返します(例外が投げられたりはしない)。 また、要素型が一致している必要はありません。
サンプルコード(要素数)
次は要素数に関連するメソッド。hasDefiniteSize メソッド
hasDefiniteSize メソッドは、その Seq オブジェクトが有限の長さを持つかどうかを返します。// hasDefiniteSize メソッド assert( Seq(0, 1, 2, 3, 4).hasDefiniteSize ) assert( !Stream(1, 2, 3).hasDefiniteSize ) // Stream は遅延評価する Seq
Stream は Seq トレイトのサブ型で遅延評価される Seq ですが、Stream オブジェクトの hasDefiniteSize メソッドは有限で最後まで評価されていなければ false を返します。 Iterator なども同様です。
length, size メソッド
length メソッドは、その Seq オブジェクトの要素数を返します。 size メソッドは Seq オブジェクトに対しては length メソッドと同じ動作をします*1。val seq = Seq(0, 1, 2, 3, 4) // length, size メソッド assert( seq.length == 5 ) assert( seq.length == seq.size) // length も size も要素数
lengthCompare メソッド
lengthCompare メソッドは、その Seq オブジェクトの長さと引数の Int 値を比べて、大小関係を表す Int 値を返します。 レシーバの Seq オブジェクトの長さが引数より大きければ正の Int 値を、同じなら0を、小さければ負の Int 値を返します。val seq = Seq(0, 1, 2, 3, 4) // lengthCompare メソッド seq lengthCompare 5 match { case x if x < 0 => println("too short!") case 0 => println("nice!") case x if x > 0 => println("too long!") } // 「nice!」と表示
返される Int 値が -1, 0, 1 に限定されてれば case の条件がもっと書きやすいんですけどね。
count メソッド
count メソッドは、引数に要素に対する述語関数を指定し、その述語関数に適合する(true を返す)要素の個数を返します。val seq = Seq(0, 1, 2, 3, 4) // count メソッド assert( seq.count(_ % 2 == 0) == 3 ) // 偶数の個数
prefixLength, segmentLength メソッド
prefixLength メソッドは、引数に要素に対する述語関数を指定し、Seq の先頭から述語関数に適合する要素の個数を返します。 述語関数に適合しない要素が現れれば、それ以降の要素は評価されません。segmentLength メソッドは、引数に要素に対する述語関数と評価を開始するインデックスを指定し、そのインデックスの位置から初めて述語関数に適合する要素の個数を返します。 評価を始める位置以外は prefixLength と同じです。
val seq = Seq(0, 1, 2, 3, 4) // prefixLength メソッド assert( seq.prefixLength(_ < 3) == 3 ) // 先頭から3未満の要素の数 ⇒ 0, 1, 2 の3個 assert( seq.prefixLength(_ > 2) == 0 ) // 先頭から2より大きい要素の数 ⇒ 0個 // segmentLength メソッド assert( seq.segmentLength(_ < 3, 2) == 1 ) // 0, 1, [[2, 3, 4]] assert( seq.segmentLength(_ > 2, 3) == 2 ) // 0, 1, 2, [[3, 4]]
以上です。
2, 3個のメソッドはシグニチャを見ただけでは動作がちょっと分かりにくいものもありましたが、基本的にはシグニチャだけで大まかな使い方は分かるものがほとんどでした。 細かいところでは空の Seq に対して呼び出したり、空の Seq を引数に渡したりするとどうなるのか?というのが気になるものもありましたが、普通に使ってる分にはあまり問題にならなさそうですね。 次回は要素へのアクセスに関するメソッドを見ていく予定。
- 作者: Martin Odersky,Lex Spoon,Bill Venners,羽生田栄一,水島宏太,長尾高弘
- 出版社/メーカー: インプレス
- 発売日: 2016/09/20
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る