公式の A Tour of Go ではじめて Go 言語を触ってみました。
以下、構造体、配列、スライス、マップについて学んだことのメモ。
前回の記事はこちら
ポインタ
- ポインタは値のメモリアドレスを指す
- 変数
T
のポインタは*T
型で、ゼロ値はnil
var p *int
-
&
演算子は、そのオペランドへのポインタを引き出す
i := 42
p = &i
-
*
演算子は、ポインタの指す先の変数を示す(間接参照)
fmt.Println(*p)
*p = 21 // ポインタ p を介して i へ値を代入する
構造体
構造体とは
- 構造体(
struct
)は、フィールドの集まり
package main
import "fmt"
type Vertex struct {
X int
Y int
}
func main() {
fmt.Println(Vertex{1, 2}) // {1 2}
}
フィールドへのアクセス
- 構造体のフィールドは、ドット
.
を用いてアクセスする
package main
import "fmt"
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
v.X = 4
fmt.Println(v.X) // 4
}
- 構造体のフィールドは、構造体のポインタを通してアクセスすることができる
- フィールド
X
を持つ struct のポインタp
- フィールド
X
を間接参照するには(*p).X
- しかしこの表記は面倒なので、Goでは代わりに
p.X
と書ける
- フィールド
package main
import "fmt"
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
p := &v // ポインタ
p.X = 1e9 // Xへの間接参照 (*p).X
fmt.Println(v) // {1000000000 2}
}
構造体リテラル
- 構造体を初期化する方法
- フィールドの初期値を
{}
内に列挙 -
{Name: }
構文を使う
- フィールドの初期値を
package main
import "fmt"
type Vertex struct {
X, Y int
}
var (
v1 = Vertex{1, 2} // has type Vertex
v2 = Vertex{X: 1} // Y:0 is implicit
v3 = Vertex{} // X:0 and Y:0
p = &Vertex{1, 2} // has type *Vertex
func main() {
fmt.Println(v1, p, v2, v3)
}
配列
配列とは
-
[n]T
型は、型T
のn
個の変数の配列を表す - 固定長
package main
import "fmt"
func main() {
var a [2]string
a[0] = "Hello"
a[1] = "World"
fmt.Println(a[0], a[1])
fmt.Println(a)
primes := [6]int{2, 3, 5, 7, 11, 13}
fmt.Println(primes)
}
スライス
スライスとは
- 型
[]T
は型T
のスライスを表す - 可変長
- より柔軟な配列と見なすことができる
- 配列からスライスを抜き出す方法
a[low:high]
-
low
を含むがhigh
は含まない(右半開区間) -
a
の要素のうち1から3を含むスライスを作る例:a[1:4]
package main
import "fmt"
func main() {
primes := [6]int{2, 3, 5, 7, 11, 13}
var s []int = primes[1:4]
fmt.Println(s)
}
- スライスは配列への参照のようなもの
- スライスはどんなデータも格納しておらず、単に元の配列の部分列を指し示している
- スライスの要素を変更すると、その元のなる配列の対応する要素が変更される
package main
import "fmt"
func main() {
names := [4]string{
"John",
"Paul",
"George",
"Ringo",
}
fmt.Println(names)
a := names[0:2] // [John Paul]
b := names[1:3] // [Paul George]
fmt.Println(a, b)
b[0] = "XXX"
fmt.Println(a, b)
fmt.Println(names)
}
スライスリテラル
- 長さのない配列リテラルのようなもの
-
[]bool{true, true, false}
- まず配列
[3]bool{true, true, false}
を作成 - 次にこの配列を参照するスライスを作成
- まず配列
package main
import "fmt"
func main() {
q := []int{2, 3, 5, 7, 11, 13}
fmt.Println(q)
r := []bool{true, false, true, true, false, true}
fmt.Println(r)
s := []struct { // 構造体のスライスリテラル
i int
b bool
}{
{2, true},
{3, false},
{5, true},
{7, true},
{11, false},
{13, true},
}
fmt.Println(s)
}
スライスの既定値
- 配列
var a [10]int
において以下のスライスは全て等価a[0:10]
a[:10]
a[0:]
a[:]
package main
import "fmt"
func main() {
s := []int{2, 3, 5, 7, 11, 13}
s = s[1:4] // [3 5 7]
fmt.Println(s)
s = s[:2] // [3 5]
fmt.Println(s)
s = s[1:] // [5]
fmt.Println(s)
}
スライスの長さと容量
- 長さ: スライスに含まれる要素の数
-
len(s)
で取得
-
- 容量: スライスの最初の要素から数えた元の配列の要素数
-
cap(s)
で取得
-
package main
import "fmt"
func main() {
s := []int{2, 3, 5, 7, 11, 13}
printSlice(s) // len=6 cap=6 [2 3 5 7 11 13]
// Slice the slice to give it zero length.
s = s[:0]
printSlice(s) // len=0 cap=6 []
// Extend its length.
s = s[:4]
printSlice(s) // len=4 cap=6 [2 3 5 7]
// Drop its first two values.
s = s[2:]
printSlice(s) // len=2 cap=4 [5 7]
// Extend its length more than the capacity
s = [:5]
printSlice(s) // error: slice bounds out of range [:5] with capacity 4
}
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
スライスのゼロ値
- スライスのゼロ値は
nil
- 0の長さ、0の容量、元の配列を持たない
make
関数によるスライスの生成
-
make
関数はゼロ化された配列を用意し、その配列を指すスライスを返す - 2番目の引数は長さ、3番目の引数は容量
package main
import "fmt"
func main() {
a := make([]int, 5)
printSlice("a", a) // len=5 cap=5 [0 0 0 0 0]
b := make([]int, 0, 5)
printSlice("b", b) // len=0 cap=5 []
c := b[:2]
printSlice("c", c) // len=2 cap=5 [0 0]
d := c[2:5]
printSlice("d", d) // len=3 cap=3 [0 0 0]
}
func printSlice(s string, x []int) {
fmt.Printf("%s len=%d cap=%d %v\n",
s, len(x), cap(x), x)
}
スライスのスライス
package main
import (
"fmt"
"strings"
)
func main() {
// Create a tic-tac-toe board.
board := [][]string{ // スライスリテラル、ただし[][]
[]string{"_", "_", "_"}, // スライスリテラル
[]string{"_", "_", "_"}, // スライスリテラル
[]string{"_", "_", "_"}, // スライスリテラル
}
// The players take turns.
board[0][0] = "X"
board[2][2] = "O"
board[1][2] = "X"
board[1][0] = "O"
board[0][2] = "X"
for i := 0; i < len(board); i++ {
fmt.Printf("%s\n", strings.Join(board[i], " "))
}
}
スライスへの要素の追加
- スライスへ新しい要素を追加するには、組み込みの
append
関数を使う -
append
の戻り値はスライス - 元の配列の容量が小さい場合は、より大きな容量の配列を割り当て直す
- 戻り値となるスライスは、新しい割当先を示すようになる
package main
import "fmt"
func main() {
var s []int
printSlice(s) // len=0 cap=0 []
// append works on nil slices.
s = append(s, 0)
printSlice(s) // len=1 cap=1 [0]
// The slice grows as needed.
s = append(s, 1)
printSlice(s) // len=2 cap=2 [0]
// We can add more than one element at a time.
s = append(s, 2, 3, 4)
printSlice(s) // len=5 cap=6 [0 1 2 3 4]
}
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
Range
-
for
ループに利用するrange
は、スライスやマップをひとつずつ反復処理するために使う - スライスを
range
で繰り返す場合、range
は反復ごとに2つの変数を返す- 1つ目の変数: インデックス
- 2つ目の変数: インデックスの場所の要素のコピー
package main
import "fmt"
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
func main() {
for i, v := range pow {
fmt.Printf("2**%d = %d\n", i, v)
}
}
- インデックスや値は
_
へ代入することで捨てることができる
for i, _ := range pow
for _, value := range pow
// インデックスだけ必要なのであれば
for i := range pow
マップ
マップ(連想配列)とは
-
map
はキーと値とを関連づける - ゼロ値は
nil
-
nil
マップはキーを持っておらず、またキーを追加することもできない
-
-
make
関数は指定された型のマップを初期化して、使用可能な状態で返す
package main
import "fmt"
type Vertex struct {
Lat, Long float64
}
var m map[string]Vertex
// var で宣言しただけの時点でキーを追加しようとすると
// panic: assignment to entry in nil map
func main() {
m = make(map[string]Vertex)
m["Bell Labs"] = Vertex{
40.68433, -74.39967,
}
fmt.Println(m["Bell Labs"]) // {40.68433 -74.39967}
}
マップリテラル
- 構造体リテラルに似ているが、キーが必要
package main
import "fmt"
type Vertex struct {
Lat, Long float64
}
var m = map[string]Vertex{
"Bell Labs": Vertex{
40.68433, -74.39967,
},
"Google": Vertex{
37.42202, -122.08408,
},
}
func main() {
fmt.Println(m) // map[Bell Labs:{40.68433 -74.39967} Google:{37.42202 -122.08408}]
}
- もし
map
に渡すトップレベルの型が単純な型名である場合は、リテラルの要素から推定できるので、その型名を省略することができる
var m = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967},
"Google": {37.42202, -122.08408},
}
マップの操作
- map
m
への操作- 要素の挿入や更新:
m[key] = elem
- 要素の取得:
elem = m[key]
- 要素の削除:
delete(m, key)
- キーに対する要素が存在するかどうかは2つ目の戻り値で確認:
elem, ok = m[key]
-
m
にkey
がある =>ok
は true -
m
にkey
がない =>ok
は false- このとき
elem
はmapの要素の型のゼロ値となる
- このとき
-
- 要素の挿入や更新:
package main
import "fmt"
func main() {
m := make(map[string]int)
m["Answer"] = 42
fmt.Println("The value:", m["Answer"]) // 42
m["Answer"] = 48
fmt.Println("The value:", m["Answer"]) // 48
delete(m, "Answer")
fmt.Println("The value:", m["Answer"]) // 0
v, ok := m["Answer"]
fmt.Println("The value:", v, "Present?", ok) // 0 false
}
関数
関数値
- Goの関数は変数である
- 引数や戻り値にとることができる
package main
import (
"fmt"
"math"
)
func compute(fn func(float64, float64) float64) float64 {
// fn: 関数の引数に「関数値」をとることができる
// fn の引数: float64, float64
// fn の戻り値: float64
return fn(3, 4) // 関数値は戻り値としても利用可能
}
func main() {
hypot := func(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
} // 関数も変数である(関数値)
fmt.Println(hypot(5, 12))
fmt.Println(compute(hypot)) // 関数 compute に関数を渡している
fmt.Println(compute(math.Pow))
}
クロージャ
- Goの関数はクロージャである
- 外部から変数を参照する関数値
- クロージャは変数へ bind されている
- 参照された変数にアクセスして変えることができる
- 外部から変数を参照する関数値
package main
import "fmt"
func adder() func(int) int {
sum := 0 // クロージャに参照されている変数
return func(x int) int {
sum += x
return sum
} // 変数 sum に bind されたクロージャを返している
}
func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
// 0 0
// 1 -2
// 3 -6
// 6 -12
// 10 -20
}
}
次回