Goはポインタを扱います。 ポインタは値のメモリアドレスを指します。
変数 T
のポインタは、 *T
型で、ゼロ値は nil
です。
var p *int
&
オペレータは、そのオペランド( operand )へのポインタを引き出します。
i := 42 p = &i
*
オペレータは、ポインタの指す先の変数を示します。
fmt.Println(*p) // ポインタpを通してiから値を読みだす *p = 21 // ポインタpを通してiへ値を代入する
これは "dereferencing" または "indirecting" としてよく知られています。
なお、C言語とは異なり、ポインタ演算はありません。
package main import "fmt" func main() { i, j := 42, 2701 p := &i // point to i fmt.Println(*p) // read i through the pointer *p = 21 // set i through the pointer fmt.Println(i) // see the new value of i p = &j // point to j *p = *p / 37 // divide j through the pointer fmt.Println(j) // see the new value of j }
struct
(構造体)は、フィールド( field )の集まりです。
package main import "fmt" type Vertex struct { X int Y int } func main() { fmt.Println(Vertex{1, 2}) }
structのフィールドは、ドット( .
)を用いてアクセスします。
package main import "fmt" type Vertex struct { X int Y int } func main() { v := Vertex{1, 2} v.X = 4 fmt.Println(v.X) }
structのフィールドは、structのポインタを通してアクセスすることもできます。
フィールド 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 fmt.Println(v) }
structリテラルは、フィールドの値を列挙することで新しいstructの初期値の割り当てを示しています。
Name:
構文を使って、フィールドの一部だけを列挙することができます(この方法でのフィールドの指定順序は関係ありません)。
例では X: 1
として X
だけを初期化しています。
&
を頭に付けると、新しく割り当てられたstructへのポインタを戻します。
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
個の変数の配列( array )を表します。
以下は、intの10個の配列を宣言しています:
var a [10]int
配列の長さは、型の一部分です。ですので、配列のサイズを変えることはできません。 これは制約のように思えますが、心配しないでください。 Goは配列を扱うための便利な方法を提供しています。
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
のスライスを表します。
コロンで区切られた二つのインデックス low と high の境界を指定することによってスライスが形成されます:
a[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] b := names[1:3] fmt.Println(a, b) b[0] = "XXX" fmt.Println(a, b) fmt.Println(names) }
スライスのリテラルは長さのない配列リテラルのようなものです。
これは配列リテラルです:
[3]bool{true, true, false}
そして、これは上記と同様の配列を作成し、それを参照するスライスを作成します:
[]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) }
スライスするときは、それらの既定値を代わりに使用することで上限または下限を省略することができます。
既定値は下限が 0 で上限はスライスの長さです。
以下の配列において
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] fmt.Println(s) s = s[:2] fmt.Println(s) s = s[1:] fmt.Println(s) }
スライスは長さ( length )と容量( capacity )の両方を持っています。
スライスの長さは、それに含まれる要素の数です。
スライスの容量は、スライスの最初の要素から数えて、元となる配列の要素数です。
スライス s
の長さと容量は len(s)
と cap(s)
という式を使用して得ることができます。
十分な容量を持って提供されているスライスを再スライスすることによって、スライスの長さを伸ばすことができます。 その容量を超えて伸ばしたときに何が起こるかを見るため、プログラム例でのスライスのいずれかの操作を変更してみてください。
package main import "fmt" func main() { s := []int{2, 3, 5, 7, 11, 13} printSlice(s) // Slice the slice to give it zero length. s = s[:0] printSlice(s) // Extend its length. s = s[:4] printSlice(s) // Drop its first two values. s = s[2:] printSlice(s) } func printSlice(s []int) { fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s) }
スライスのゼロ値は nil
です。
nil
スライスは 0 の長さと容量を持っており、何の元となる配列も持っていません。
package main import "fmt" func main() { var s []int fmt.Println(s, len(s), cap(s)) if s == nil { fmt.Println("nil!") } }
スライスは、組み込みの make
関数を使用して作成することができます。
これは、動的サイズの配列を作成する方法です。
make
関数はゼロ化された配列を割り当て、その配列を指すスライスを返します。
a := make([]int, 5) // len(a)=5
make
の3番目の引数に、スライスの容量( capacity )を指定できます。
cap(b)
で、スライスの容量を返します:
b := make([]int, 0, 5) // len(b)=0, cap(b)=5 b = b[:cap(b)] // len(b)=5, cap(b)=5 b = b[1:] // len(b)=4, cap(b)=4
package main import "fmt" func main() { a := make([]int, 5) printSlice("a", a) b := make([]int, 0, 5) printSlice("b", b) c := b[:2] printSlice("c", c) d := c[2:5] printSlice("d", d) } 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], " ")) } }
スライスへ新しい要素を追加するには、Goの組み込みの append
を使います。
append
についての詳細は documentation
を参照してみてください。
func append(s []T, vs ...T) []T
上の定義を見てみましょう。 append
への最初のパラメータ s
は、追加元となる T
型のスライスです。
残りの vs
は、追加する T
型の変数群です。
append
の戻り値は、追加元のスライスと追加する変数群を合わせたスライスとなります。
もし、元の配列 s
が、変数群を追加する際に容量が小さい場合は、より大きいサイズの配列を割り当て直します。
その場合、戻り値となるスライスは、新しい割当先を示すようになります。
(スライスについてより詳しく学ぶには、Slices: usage and internalsを読んでみてください)
package main import "fmt" func main() { var s []int printSlice(s) // append works on nil slices. s = append(s, 0) printSlice(s) // The slice grows as needed. s = append(s, 1) printSlice(s) // We can add more than one element at a time. s = append(s, 2, 3, 4) printSlice(s) } func printSlice(s []int) { fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s) }
for
ループに利用する range
は、スライスや、マップ( map
)をひとつずつ反復処理するために使います。
スライスをrangeで繰り返す場合、rangeは反復毎に2つの変数を返します。 1つ目の変数はインデックス( index )で、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) } }
インデックスや値は、 " _
"(アンダーバー) へ代入することで捨てることができます。
もしインデックスだけが必要なのであれば、 " , value
" を省略します。
package main import "fmt" func main() { pow := make([]int, 10) for i := range pow { pow[i] = 1 << uint(i) // == 2**i } for _, value := range pow { fmt.Printf("%d\n", value) } }
Pic
関数を実装してみましょう。
このプログラムを実行すると、生成した画像が下に表示されるはずです。
この関数は、長さ dy
のsliceに、各要素が8bitのunsigned int型で長さ dx
のsliceを割り当てたものを返すように実装する必要があります。
画像は、整数値をグレースケール(実際はブルースケール)として解釈したものです。
生成する画像は、好きに選んでください。例えば、面白い関数に、 (x+y)/2
、 x*y
、 x^y
などがあります。
ヒント:( [][]uint8
に、各 []uint8
を割り当てるためにループを使用する必要があります)
ヒント:( uint8(intValue)
を型の変換のために使います)
package main import "golang.org/x/tour/pic" func Pic(dx, dy int) [][]uint8 { } func main() { pic.Show(Pic) }
map
はキーと値とを関連付けます(マップします)。
マップのゼロ値は nil
です。
nil
マップはキーを持っておらず、またキーを追加することもできません。
make
関数は指定された型の、初期化され使用できるようにしたマップを返します。
package main import "fmt" type Vertex struct { Lat, Long float64 } var m map[string]Vertex func main() { m = make(map[string]Vertex) m["Bell Labs"] = Vertex{ 40.68433, -74.39967, } fmt.Println(m["Bell Labs"]) }
mapリテラルは、structリテラルに似ていますが、 キー ( key )が必要です。
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に渡すトップレベルの型が単純な型名である場合は、リテラルの要素から推定できますので、その型名を省略することができます。
package main import "fmt" type Vertex struct { Lat, Long float64 } var m = map[string]Vertex{ "Bell Labs": {40.68433, -74.39967}, "Google": {37.42202, -122.08408}, } func main() { fmt.Println(m) }
map m
の操作を見ていきましょう。
m
へ要素(elem)の挿入や更新:
m[key] = elem
要素の取得:
elem = m[key]
要素の削除:
delete(m, key)
キーに対する要素が存在するかどうかは、2つの目の値で確認します:
elem, ok = m[key]
もし、 m
に key
があれば、変数 ok
は true
となり、存在しなければ、 ok
は
false
となります。
なお、mapに key
が存在しない場合、 elem
はmapの要素の型のゼロ値となります。
Note: もし elem
や ok
を宣言していないのであれば、次のように :=
で短く表現できます:
elem, ok := m[key]
package main import "fmt" func main() { m := make(map[string]int) m["Answer"] = 42 fmt.Println("The value:", m["Answer"]) m["Answer"] = 48 fmt.Println("The value:", m["Answer"]) delete(m, "Answer") fmt.Println("The value:", m["Answer"]) v, ok := m["Answer"] fmt.Println("The value:", v, "Present?", ok) }
WordCount
関数を実装してみましょう。string s
で渡される文章の、各単語の出現回数のmapを返す必要があります。
wc.Test
関数は、引数に渡した関数に対しテストスイートを実行し、成功か失敗かを結果に表示します。
strings.Fields で、何かヒントを得ることができるはずです。
Note: このテストスイートで何を入力とし、何を期待しているかについては、golang.org/x/tour/wcを見てみてください。
package main import ( "golang.org/x/tour/wc" ) func WordCount(s string) map[string]int { return map[string]int{"x": 1} } func main() { wc.Test(WordCount) }
関数も変数です。他の変数のように関数を渡すことができます。
関数値( function value )は、関数の引数に取ることもできますし、戻り値としても利用できます。
package main import ( "fmt" "math" ) func compute(fn func(float64, float64) float64) 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)) fmt.Println(compute(math.Pow)) }
Goの関数は クロージャ( closure ) です。 クロージャは、それ自身の外部から変数を参照する関数値です。 この関数は、参照された変数へアクセスして変えることができ、その意味では、その関数は変数へ"バインド"( bind )されています。
例えば、 adder
関数はクロージャを返しています。
各クロージャは、それ自身の sum
変数へバインドされます。
package main import "fmt" func adder() func(int) int { sum := 0 return func(x int) int { sum += x return sum } } func main() { pos, neg := adder(), adder() for i := 0; i < 10; i++ { fmt.Println( pos(i), neg(-2*i), ) } }
関数を用いた面白い例を見てみましょう。
fibonacci
(フィボナッチ)関数を実装しましょう。この関数は、連続するフィボナッチ数(0, 1, 1, 2, 3, 5, ...)を返す関数(クロージャ)を返します。
package main import "fmt" // fibonacci is a function that returns // a function that returns an int. func fibonacci() func() int { } func main() { f := fibonacci() for i := 0; i < 10; i++ { fmt.Println(f()) } }
この章はこれで終わりです。