倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

List の getAt() をしつこくやるよ

最近 GDK が List に追加しているメソッドをあれこれ試しているんですが、Java の配列のように要素を取得できる [] 演算子、すなわち getAt() メソッドにいろいろなインデックスを渡したときの挙動がいまいちシックリこないので、もう少しあれこれ試してみました。 putAt() も同じような挙動だと思いますが今回は getAt() のみ。

int 値のインデックス

まずはインデックスとして int 値を渡した場合。これは解くに問題ないですね。 たとえばサイズ4のリストに対して int 値のインデックスを渡した場合を考えてみましょう:

int n = 4
def list = 0..<n    // [0, 1, 2, 3]

(-n-1..n).each{ i ->    // -4 ≦ i ≦ 3 で要素が返される(-5は例外、4はnull)
    print(list[i])
}

これを実行すると、各 i の値に対して以下の結果が得られます:

i list[i]
-5 -
-4 0
-3 1
-2 2
-1 3
0 0
1 1
2 2
3 3
4 null
  • -4 ≦ i ≦ -1(サイズ n のリストの場合は -n ≦ i ≦ -1)の負のインデックスを渡した場合は、末尾から数えて -i 番(-1 なら末尾から数えて -(-1) = 1 番目)の要素が返される(つまりは i + n 番目の要素が返される)
  • -5(サイズ n の場合は -n-1 )以下のインデックスを渡した場合は例外 (ArrayIndexOutOfBoundsException) が投げられる
  • 4(サイズ n の場合は n)以上のインデックスを渡した場合は null を返す

となってます。

末尾を含む範囲のインデックス i..j

次は末尾を含む範囲 i..j をインデックスとして渡した場合の返り値を見ていきます。 例外が投げられずにリストを返す i, j の値は、サイズ n のリストに対して -n ≦ i, j ≦ n-1 の場合。 その範囲外では例外が投げられます。

int n = 4
def list = 0..<n    // [0, 1, 2, 3]

(-n-1..n).each{ i ->    // -4 ≦ i ≦ 3 で返り値を返しうる(端の-5, 4 で例外)
    (-n-1..n).each{ j ->    // -4 ≦ j ≦ 3 で返り値を返しうる(端の-5, 4 で例外)
        print(list[i..j])
    }
    println()
}

このコードを単に実行しても例外が投げられて終わりですが、適当に処理を追加して結果を表にすると以下のようになります:

i\j -5 -4 -3 -2 -1 0 1 2 3 4
-5 - - - - - - - - - -
-4 - [0] [0,1] [0,1,2] [0,1,2,3] [0] [0,1] [0,1,2] [0,1,2,3] -
-3 - [1,0] [1] [1,2] [1,2,3] [1,0] [1] [1,2] [1,2,3] -
-2 - [2,1,0] [2,1] [2] [2,3] [2,1,0] [2,1] [2] [2,3] -
-1 - [3,2,1,0] [3,2,1] [3,2] [3] [3,2,1,0] [3,2,1] [3,2] [3] -
0 - [0] [0,1] [0,1,2] [0,1,2,3] [0] [0,1] [0,1,2] [0,1,2,3] -
1 - [1,0] [1] [1,2] [1,2,3] [1,0] [1] [1,2] [1,2,3] -
2 - [2,1,0] [2,1] [2] [2,3] [2,1,0] [2,1] [2] [2,3] -
3 - [3,2,1,0] [3,2,1] [3,2] [3] [3,2,1,0] [3,2,1] [3,2] [3] -
4 - - - - - - - - - -
  • 「-」の部分は例外が投げられる
  • 赤い部分は逆順序のリストが返される部分
  • i, j ともに -4~-1 の部分と 0~3の部分が同じになっています(サイズnの場合は -n~-1 の部分と 0~n-1 の部分)
  • インデックスとして負の整数を使わない場合に対応するのは表の右下4分の1の箇所(かつ赤くない部分)

範囲を指定する際に両方を負の整数にするなんてあんまり意味なさそうですが、まぁそういう場合もリストを返す場合がありますよ、と。 範囲の端(from と to)が負なら、それぞれリストのサイズ n を加えたものを新たな範囲の端として使用して、対応する結果を返す感じですね。

末尾を含まない範囲のインデックス i..<j

末尾を含まない範囲 i..<j の場合は j の値として1つ小さい値を設定した場合の、末尾を含むインデックスになりそうですが、というか大体はなるのですが、少々違う箇所があります。 まず i = j のときは i..<i は空範囲となり、返り値は空リストになります。 これは末尾を含む範囲にはあり得なかったものです。 また、j = i ± 1 のときは i だけからなる範囲になります。 これは末尾を含む範囲の場合の i = j と同じになります。 これら2つの場合がちょっと注意が必要かと。

int n = 4
def list = 0..<n    // [0, 1, 2, 3]

(-n-1..n).each{ i ->    // -4 ≦ i ≦ 3 で返り値を返しうる(端の-5, 4 で例外)
    (-n-2..(n+1)).each{ j ->    // -5 ≦ j ≦ 4 で返り値を返しうる(端の-6, 5 で例外)
        println(list[i..<j])
    }
    println()
}

範囲を含む場合と同様に結果を表にすると以下のようになります:

i\j -6 -5 -4 -3 -2 -1 0 1 2 3 4 5
-5 - [] - - - - - - - - - -
-4 - [0] [] [0] [0,1] [0,1,2] [0,1,2,3] [0] [0,1] [0,1,2] [0,1,2,3] -
-3 - [1,0] [1] [] [1] [1,2] [1,2,3] [1,0] [1] [1,2] [1,2,3] -
-2 - [2,1,0] [2,1] [2] [] [2] [2,3] [2,1,0] [2,1] [2] [2,3] -
-1 - [3,2,1,0] [3,2,1] [3,2] [3] [] [3] [3,2,1,0] [3,2,1] [3,2] [3] -
0 - [0] [0,1] [0,1,2] [0,1,2,3] [0] [] [0] [0,1] [0,1,2] [0,1,2,3] -
1 - [1,0] [1] [1,2] [1,2,3] [1,0] [1] [] [1] [1,2] [1,2,3] -
2 - [2,1,0] [2,1] [2] [2,3] [2,1,0] [2,1] [2] [] [2] [2,3] -
3 - [3,2,1,0] [3,2,1] [3,2] [3] [3,2,1,0] [3,2,1] [3,2] [3] [] [3] -
4 - - - - - - - - - - [] -
  • 「i = j」の左上から右下に走る対角線の部分が空範囲の指定に対応し、空リスト [] を返しています
  • 「j = i ± 1」 となる、上記の「i = j」 の上下の斜め線の部分が要素が i 1つからなる範囲に対応し、返り値のリストは1つの要素のみを持ちます
  • それ以外は末尾を含む範囲の場合で j の値を1つずらした場合に対応。 対角線に対して右上と左下の三角形でずれ方が逆ですけどね。

んー、なんか表にしてみると前より理解できたような、余計に分からなくなったような・・・

プログラミングGROOVY

プログラミングGROOVY