スライス

スライスは配列に似ているが、要素を追加することによって大きさ(長さ)を変更できるデータ構造。大きさの変更は自動的になされる。小さくすることはできない。たぶん。

スライスの宣言は次のように大きさを指定せずに行う。

var 変数名 []型

同時に初期化するには:

var 変数名 []型 = []型{ 値1, 値2, 値3, ... }
var 変数名 = []型{ 値1, 値2, 値3, ... }

大きさを指定しないことを除けば、配列と一緒だな。ちょっと試してみよう。

package main

import "fmt"

func main() {
    var s1 []int
    var s2 []int = []int{ 1,2,3,4,5 }
    var s3 = []int{ 6,7,8,9,10 }

    fmt.Println(s1)
    fmt.Println(s2)
    fmt.Println(s3)
}
^o^ > go run slice.go
[]
[1 2 3 4 5]
[6 7 8 9 10]

スライスはまた、配列の部分列を取り出すことでも作れる。

操作 意味
s[m:n] m から n – 1 まで
s[m:] m から最後尾まで
s[:n] 先頭から n – 1 まで
s[:] 先頭から最後尾まで
package main

import "fmt"

func main() {
    var array = [8]int{ 1,2,3,4,5,6,7,8 }

    var s1 = array[2:6]
    var s2 = array[3:]
    var s3 = array[:3]
    var s4 = array[:]

    fmt.Println(s1)
    fmt.Println(s2)
    fmt.Println(s3)
    fmt.Println(s4)
}
^o^ > go run slice2.go
[3 4 5 6]
[4 5 6 7 8]
[1 2 3]
[1 2 3 4 5 6 7 8]

スライスを配列の部分列として取り出した場合、元の配列とデータ格納領域を共有することに注意。つまり、スライスの要素を変更すると、元の配列の対応する要素も変更される。

package main

import "fmt"

func main() {
    var array = [4]int{ 1,2,3,4 }

    fmt.Println(array)

    var s = array[:]
    s[2] = 10

    fmt.Println(s)
    fmt.Println(array)
}
^o^ > go run slice3.go
[1 2 3 4]
[1 2 10 4]
[1 2 10 4]

配列

配列は次のように大きさ(長さ)と要素の型を合わせて宣言する。

var 変数名 [大きさ]型

要素の型が決まっているので、同じ配列に異なる型の要素を格納することはできない。
また、大きさも含めて配列の型なので、要素の型が同じでも大きさが異なれば別の型とみなされる。例えば [4]int[8]int は別の型だ。

宣言と同時に初期化するには次のようにする。

var 変数名 [大きさ]型 = [大きさ]型{ 値0, 値1, 値2, 値3, ... }

または

var 変数名 = [大きさ]型{ 値0, 値1, 値2, 値3, ... }

初期化しないと、配列の要素はゼロ値になる。

少し試してみよう。

package main

import "fmt"

func main() {
    var a [4]int
    var b [6]int = [6]int{ 0, 1, 2, 3, 4, 5 }
    var c = [8]int { 0, 1, 2, 3, 4, 5, 6, 7 }

    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)
}
o^ > go run array.go
[0 0 0 0]
[0 1 2 3 4 5]
[0 1 2 3 4 5 6 7]

配列の要素にアクセスするには、[] を使ってインデックスを指定する。インデックスは 0 始まりだ。

package main

import "fmt"

func main() {
    var a [4]int = [4]int{ 1, 2, 3, 4 }

    fmt.Println(a[2])
    fmt.Println(a)

    a[1] = 7

    fmt.Println(a[1])
    fmt.Println(a)
}
^o^ > go run array2.go
3
[1 2 3 4]
7
[1 7 3 4]

最後に len 関数について書いておこう。len 関数は配列の大きさ(長さ)を返す。

package main

import "fmt"

func main() {
    var a [4]int = [4]int{ 1, 2, 3, 4 }

    fmt.Println(a)
    fmt.Println(len(a))
}
^o^ > go run array3.go
[1 2 3 4]
4

switch文

条件分岐には if 文のほかにもうひとつ、switch 文がある。switch 文は次の通り。

switch 条件式 {
    case 式A:
        処理A1
        処理A2
        処理A3
    case 式B:
        処理B1
        処理B2
        処理B3
    case 式C:
        処理C1
        処理C2
        処理C3
    default:
        処理Z1
        処理Z2
        処理Z3
}

条件式の値と case 節の式A、B、C の値が順に比較され、等しくなる最初の case 節の処理が実行される。どの case 節の式とも等しくなければ default 節が実行される。
試してみよう。お題は FizzBuzz 問題だ。

package main

import "fmt"

func main() {
    i := 1

    for i <= 20 {
        switch i % 15 {
            case 0:
                fmt.Println("FizzBuzz")
                i++
            case 3, 6, 9, 12:
                fmt.Println("Fizz")
                i++
            case 5, 10:
                fmt.Println("Buzz")
                i++
            default:
                fmt.Println(i)
                i++
        }
    }
}
^o^ > go run switch.go
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz

例題のために冗長な書き方にしたけど、期待通り動いている。見ればわかるように、case 節の式はひとつでなくてもいい。
また、条件式を書かない書き方もある。この場合には case 節の式の値が true になる最初の節が実行される。

package main

import "fmt"

func main() {
    i := 1

    for i <= 20 {
        switch {
            case i % 15 == 0:
                fmt.Println("FizzBuzz")
            case i % 3 == 0:
                fmt.Println("Fizz")
            case i % 5 == 0:
                fmt.Println("Buzz")
            default:
                fmt.Println(i)
        }
        i++
    }
}
^o^ > go run switch2.go
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz

無限ループとbreak、continue

for 文で、条件部を省略してただ for と書くと無限ループになる。

for {
    処理
}

ループの制御として、breakcontinue がある。break はループを抜け、continue はループの頭に戻る。
試してみよう。

package main

import "fmt"

func main() {
    i := 0

    for {
        if i > 10 {
            break
        } else if i % 3 == 0 {
            i++
            continue
        } else {
             fmt.Println(i)
        }
        i++
    }
}
^o^ > go run for_inf.go
1
2
4
5
7
8
10

for文

Go の繰り返し構文には for しかない。だけどこの for には 3 通りの使い方があって、ひとつは普通の for、ひとつは while 的なもの、そしてもひとつはいわゆる for each 的なものだ。
順番に見ていこう。

まずひとつめ、普通の for。「普通の」というのは、繰り返し用変数の初期化と繰り返し条件、変数の更新処理がある C や JavaScript なんかと同じ、くらいの意味。ただしカッコは要らない。

package main

import "fmt"

func main() {
    names := []string{ "Andy", "Bill", "Charlie" }

    for i := 0; i < 3; i++ {
        fmt.Println(names[i])
    }
}

names := []string{ "Andy", "Bill", "Charlie" } というところは、スライスっていう配列みたいなものを作ってるんだけど、これについては別の機会に書く。
実行してみよう。

^o^ > go run for.go
Andy
Bill
Charlie

ふたつめの使い方は while のようなもの。というか while そのものだ。for の後に繰り返し条件だけを書く。

package main

import "fmt"

func main() {
    names := []string{ "Andy", "Bill", "Charlie" }

    i := 0
    for i < 3 {
        fmt.Println(names[i])
        i++
    }
}
^o^ > go run for_while.go
Andy
Bill
Charlie

最後はいわゆる for each 的な使い方。range キーワードを使って、スライス(または配列)のインデックスと要素を一つずつ取り出して繰り返す。

package main

import "fmt"

func main() {
    names := []string{ "Andy", "Bill", "Charlie" }

    for idx, name := range names {
        fmt.Println(idx, name)
    }
}
^o^ > go run for_range.go
0 Andy
1 Bill
2 Charlie

ここではインデックスを idx 変数で受け取っているけど、場合によっては使わないこともある。そういう時は次のように _ (アンダースコア)で受けてやる。そうしないと idx 変数を使っていない、という警告を受けることになる。アンダースコアは、「ここに入る値は使わないよ」ということを示すものだ。

for _, name := range names {
    fmt.Println(name)
}

グローバル変数

変数を関数の外で宣言するとグローバル変数になる。グローバル変数は、同じファイル内のどの関数からでもアクセスできる。

package main

import "fmt"

var name = "Andy"

func hello() {
    fmt.Println("Hello, " + name + "!")
}

func main() {
    hello()
    fmt.Println("Good morning, " + name + ".")
}
^o^ > go run var_global.go
Hello, Andy!
Good morning, Andy.

関数の中で宣言されているのはローカル変数。ローカル変数はその関数の中だけで使える。

変数の宣言と初期化(2)

先日、変数の宣言と初期化を一緒に行うには「var 変数 = 値」という書き方をする、てなことを書いた。
これ以外に、関数の中に限っては「変数 := 値」という書き方ができる。

package main

import "fmt"

func main() {
    name := "Andy"

    fmt.Println("Hello, " + name + "!")
}
^o^ > go run var4.go
Hello, Andy!

if文

if 文はいたって普通。else ifelsif でも elif でもなく else if

package main

import "fmt"

func isZero(n int) string {
    if n > 0 {
        return "More than zero."
    } else if n < 0 {
        return "Less than zero."
    } else {
        return "Just ZERO!"
    }
}

func main() {
    fmt.Println(isZero(3))
    fmt.Println(isZero(-1))
    fmt.Println(isZero(0))
}
^o^ > go run if.go
More than zero.
Less than zero.
Just ZERO!

関数

関数の定義には func キーワードを使う。

func 関数名(引数 型...) 返り値の型 {
    処理
}

引数、返り値とも型を後に書く。珍しいな。
値を返すには return を使う。
試してみよう。

package main

import "fmt"

func hello(name string) string {
    return "Hello, " + name + "!"
}

func main() {
    fmt.Println(hello("Andy"))
}
^o^ > go run func.go
Hello, Andy!