Go言語の簡易スライス式(s[n:m])について


下記のコードにおいて、(2)はOKで、(3)がruntime errorになるのはどのような理由によるものでしょうか？ Goのバージョンは"go1.11
darwin/amd64"です。

[code]

    package main
    
    import "fmt"
    
    func main() {
        a := "a"
        fmt.Println(a[:len(a)])      // (1) "a"
        fmt.Println(a[len(a):])      // (2) ""
        // fmt.Println(a[len(a)])    // (3) panic - index out of range
        // fmt.Println(a[len(a)+1:]) // (4) panic - slice bounds out of range
    }
    
[/code]




Go言語はほとんど使ったことがないんですが、

  * 配列のインデックスとしての有効値は`0`から`len(a)-1`まで
  * 範囲を指定する場合、(左端)→開始位置のインデックス、(右端)→終了位置のインデックス **+1** で指定する

と言う場合には同じなので。

要素が複数あった方がわかりやすいように思います。

[code]

    package main
    
    import (
        "fmt"
    )
    
    func main() {
        a := "abc"
        fmt.Println(len(a)) //->3
        fmt.Println(a[0]) //->97 ('a')
        fmt.Println(a[1]) //->98 ('b')
        fmt.Println(a[2]) //->99 ('c')
        //fmt.Println(a[3]) //panic: runtime error: index out of range
        fmt.Println(a[0:]) //->abc
        fmt.Println(a[:3]) //->abc
        fmt.Println(a[0:3]) //->abc
        fmt.Println(a[1:]) //->bc
        fmt.Println(a[1:3]) //->bc
        fmt.Println(a[2:]) //->c
        fmt.Println(a[2:3]) //->c
        fmt.Println(a[3:]) //->
        fmt.Println(a[3:3]) //->
        //fmt.Println(a[4:]) //panic: runtime error: slice bounds out of range
    }
    
[/code]

今の場合`a`(`len(a)==3`)のインデックスはこのように割り振られます。

[code]

    |'a'|'b'|'c'|何もない
     [0] [1] [2] [3]
    
[/code]

`a[len(a)]`(==`a[3]`)は要素のないところですから当然エラーになります。

だとすると逆に`a[:len(a)]`や`a[len(a):]`はなぜエラーにならないのかですが、上にあげた2番目のルールがあるからです。

`a[:len(a)]`は`len(a)==3`のとき、`a[0:3]`と同じです。

[code]

    |'a'|'b'|'c'|何もない
     [0]←ここから
                 [3]←ここの1個前まで
    
[/code]

`a[3]`には要素は何もないのですが、範囲としてはその1個前までを表すので"abc"を表すことになります。

当然`3`が指定できないと、「最後まで全部」を表す範囲が明示的には書けないことになりますから、範囲の終点としては指定できないと困ります。

また`a[0:3]`が`"abc"`、`a[1:3]`が`"bc"`、そして`a[2:3]`が`"c"`を表すのですから`a[3:3]`が空文字列を表すとするのは極めて自然だと思えないですか?
(ま、慣れるまでは思えないかもしれませんが…。)

と言うわけで(人間にとってと言うより機械の都合にとってと言う感じですが)`3`は範囲の始点を表す時、(必ず終点も同じでないといけませんが)有効な値ということになります。

[code]

    a[0:]
    |'a'|'b'|'c'|何もない
     [0]←ここから全部(`"abc"`)
    
    a[3:]
    |'a'|'b'|'c'|何もない
                 [3]←ここから全部(`""`)
    
[/code]

あなたのあげた例の場合は`len(a)==1`なのでかえってピンとこないかもしれませんがほとんど同じことです。

[code]

    a[:0] (==a[:len(a)]
    |'a'|何もない
         [1]←この1個前まで全部("a")
    
[/code]

[code]

    a[0:] (==a[len(a):]
    |'a'|何もない
         [1]←ここから全部("")
    
[/code]

* * *

なんでこんなわかりにくい範囲指定をするのかは、その方が色々便利だから、ですが、その辺はこのやり方で経験を積まれれば、自然に見えてくるかと思います。

それ他の点で何か分かりにくい点があれば、お知らせください。

