A tour Of Go メモ
変数の宣言はvarか、:=での代入
constはプリミティブな値にしか使えない
character, string, boolean, numeric
switchは引数を取らずに実行できる
if~else句を見やすく書くために使える
code:go
switch {
case i < 0:
fmt.Println("i is less than 0")
case i >= 0:
fmt.Println("i is equal or greater than 0")
case default:
fmt.Println("default")
}
ポインタ
code:go
// int型のポインタ
var p *int
i := 42
// &オペレータはオペランドへのポインタを引き出す、この場合はi
p = &i
// *オペレータは、ポインタの指す先の変数を示す
// ポインタpを通してiから値を読み出す
fmt.Println(*p)
// ポインタpを通してiへ値を代入する
*p = 21
ポインタ演算はない
アドレス加算して別のポインタを参照するやつのこと
ポインタを出力しても、メモリアドレスが出てきたりはしないっぽい
構造体
フィールドの集まり。Goにクラスはない
code:go
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
v.X = 4
fmt.Println(v.X)
//structのフィールドは、structのポインタを通じてアクセスできる
p := &v
p.X = 1e9
fmt.Println(v)
}
structリテラルは、フィールドの値を列挙することで、新しいstructの初期値の割り当てを示す
code:go
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
// &を頭につけると、新しく割り当てられたstructへのポインタを返す
p = &Vertex{1, 2} // has type *Vertex
)
func main() {
fmt.Println(v1, p, v2, v3)
}
スライス
Arrayより一般的らしい
スライスは可変長の配列
宣言の仕方で変わる、長さを指定しない場合スライスになる
code:go
var s []int
code:go
// arrayからsliceを作れる
primes := 6int{2, 3, 5, 7, 11, 13} fmtp.Println(s)
スライスは配列への参照のようなもの
スライスはどんなデータも格納しておらず、単に元の配列の部分列を指し示している
スライスの要素を変更すると、その元となる配列の対応する要素が反映される
スライスのリテラルは長さのない配列リテラルのようなもの
code:go
// これは配列リテラル
// これは上記と同様の配列を作成し、それを参照するスライス
[]bool{true, true, false}
スライスのデフォルト
スライスするときは、それらの規定値を代わりに使用することで上限または下限を省略することができる
規定値は下限が0で上限はスライスの長さ
code:go
//以下の配列において、これらのスライス式は等価
スライスの話めっちゃ長いosamtimizer.icon
スライスは長さlengthと容量capacityの両方を持っている
スライスの長さは、それに含まれる要素の数
スライスの容量は、スライスの最初の要素から数えて、元となる配列の要素数
それぞれ、len(s)とcap(s)という式で得られる
関数じゃなくて式なんだosamtimizer.icon
十分な容量を持って提供されているスライスを再スライスすることによって、スライスの長さを伸ばすことができる
また、容量を超える長さを持つスライスは作れない
逆に言うと、容量の限りまで長さを伸ばすことはできる
スライスが変わる条件
スライス式の左が0もしくは記述しない場合、capacityが変わることはない
s[:6]、 s[0:2]は変化しない
s[1:4]、s[4:]は変化する
Nil Slice
スライスのゼロ値はnil
nilスライスは0の長さと容量を持っており、何の元となる配列も持っていない
code:go
// 宣言しただけだとNil slice
var s[]int
makeでスライスを作る
組み込みのmake関数を使用してスライスが作れる
これは動的サイズの配列を作成する方法
make関数はゼロ化された配列を割り当て、その配列を指すスライスを返す
code:go
a := make([]int, 5) //len(a)=5
makeの3番目の引数に、容量を指定できる
code:go
b := make([]int, 0, 5) //len(b)=0, cap(b)=5
b = b1: // len(b)=4, cap(b)=4 スライスのスライス
スライスは、他のスライスを含む任意の型を含むことができる
code:go
board := [][]string{
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
}
スライスに新しい要素を追加する
組み込みのappendを使う、第一引数にスライスを取り、残りのvsは追加するT型の変数群
新しいスライスを生成して返す
code:go
func append(s, []T, vs ...T) []T
まだまだ奥が深いslice
Range
forループに利用する
スライスやmapを一つずつ反復処理するために使うもの
スライスをrangeで繰り返す場合は、rangeは反復ごとに2つの変数を返す
1つ目はindex, 2つ目はindexの場所の要素のコピー
code:go
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
for i, v := range pow {
fmt.Printf("2**%d = %d"\n", i, v)
}
code:output
2**0 = 1
2**1 = 2
2**2 = 4
2**3 = 8
2**4 = 16
2**5 = 32
2**6 = 64
2**7 = 128
Range continued
インデックスや値は、_で捨てられる
code:go
for i, _ := range pow
for _, v := range pow
//ちなみに省略もできる
for i := range pow
Exercise: Slices
これ課題がちょっと分かりづらいんだけど、画像を生成する関数をPic.showに渡すようだった
code:go
package main
import "golang.org/x/tour/pic"
func Pic(dx, dy int) [][]uint8 {
pic := make([][]uint8, dy, dy)
for y := 0; y < dy; y++ {
row := make([]uint8, dx)
for x := 0; x < dx; x++ {
}
}
return pic
}
func main() {
pic.Show(Pic)
}
https://gyazo.com/39dbe6b985f46942257640e38a74b868
ほげー
Maps
mapはキーと値とを関連づける
マップのゼロ値はnilで、nilマップはキーを持っておらず、追加もできない
make関数は指定された型のマップを初期化して、使用可能な状態で返す
code:go
type Vertext struct {
Lat, Long float64
}
//キーがstringで、値がVertexなマップ
func main() {
40.68433, -74.39967
}
}
mapリテラルはstructリテラルににているが、キーが必要
code:go
type Vertex struct {
Lat, Long float64
}
"Bell Labs": Vertex{
40.68433, -74.39967,
},
"Google": Vertex{
37.42202, -122.08408,
},
}
func main() {
fmt.Println(m)
}
もしもmapに渡すトップレベルの型が単純な型名である場合は、リテラル要素から推定できるために省略可能
code:go
"Bell Labs": {40.68433, -74.39967},
"Google": {37.42202, -122.08408},
}
func main() {
fmt.Println(m)
}
Mutating Maps
map mへの操作を例にとる
code:go
//mへ要素の挿入や更新
//要素の取得
//要素の削除
delete(m, key)
//キーに対する存在性の確認、二つ目の値で確認する。okはbool
//elemは値が取れなかった場合、mapの要素の型のゼロ値になる
exercise: Maps
code:go
package main
import (
"golang.org/x/tour/wc"
"strings"
)
func WordCount(s string) mapstringint { slice := strings.Fields(s)
for _, v := range slice {
if ok {
} else {
}
}
return result
}
func main() {
wc.Test(WordCount)
}
Function Values
関数も変数
code:go
// computeは関数fnを引数にとり、 floatを返す関数
func compute(fn func(float64, float64) float64) float64 {
return fn(3, 4)
}
function closures
Goの関数はクロージャ
クロージャはそれ自身の外部から変数を参照する関数値
この関数は、参照された変数へアクセスして変えることができ、その意味では、その関数は変数へバインドされる
例えば、adder関数はクロージャを返す
各クロージャは、それ自身のsum変数へバインドされる
code:go
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)
)
}
}
Exercise: Fibonacci closure
Methods
クラスはないけど、型にメソッドを定義できる
メソッドはレシーバ引数を伴う関数
code:go
type Vertex struct {
X, Y float64
}
//(v Vertex)がレシーバのAbsメソッドを定義
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := Vertex{3, 4}
fmt.Println(v.Abs())
}
メソッドじゃなくて普通の関数として定義したらこう
code:go
type Vertex struct {
X, Y float64
}
func Abs(v Vertex) float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := Vertex{3, 4}
fmt.Println(Abs(v))
}
構造体だけでなく、任意の型にもメソッドを宣言できる
ただし、レシーバ型が同じパッケージにある必要がある
code:go
type MyFloat float64
func (f MyFloat) Abs() float64{
...
}
ポインタレシーバでメソッドを宣言できる
レシーバの型が、ある型Tへの構文*Tがあることを示す
なおTは*intのようなポインタ自身を取ることはできない
code:go
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v := Vertex{3, 4}
v.Scale(10)
fmt.Println(v.Abs())
}
ポインタレシーバを持つメソッドは、レシーバが指す変数を変更できる
レシーバ自身を更新する場合はポインタレシーバである必要がある
変数レシーバでは、メソッドの操作は元の変数のコピーを操作する(要するに変更できない
ポインタを引数に取る関数は、ポインタを渡す必要がある
code:go
var v Vertex
ScaleFunc(v, 5) // Compile Error
ScaleFunc(&v, 5) // OK
しかし、メソッドがポインタレシーバである場合、呼び出し時に変数、またはポインタのいずれかのレシーバとして取ることができる
code:go
var v Vertex
v.Scale(5)
p := &v
p.Scale(10)
逆も見てみる
変数の引数をとる関数は、特定の型の変数を取る必要がある
code:go
var v Vertex
fmt.Println(AbsFunc(v)) // OK
fmt.Println(AbsFunc(&v)) // Compile Error
メソッドが変数レシーバである場合、どちらでもOK
code:go
var v Vertex
fmt.Println(v.Abs())
p := &v
fmt.Println(p.Abs()) // OK
ポインタレシーバを使うときの二つの理由
メソッドがレシーバが指す先の変数を変更するため
メソッドの呼び出しごとに変数のコピーが発生するのを避けるため
でかいStructだったらパフォーマンスに影響あるかもね
一般的には、値レシーバ、またはポインタレシーバのどちらかで全てのメソッドを与え(実装しろ、と言うこと?)、混在させるべきではない
Interfaces
code:go
type Abser interface {
Abs() float64
}
func main() {
var a Abser
f := MyFloat(-math.Sqrt2)
v := Vertex{3, 4}
a = f
a = &v
// VertexはAbser interfaceを満たしていないため、Compile Error
// これちょっと複雑で、*VertexはAbserを満たしているので a = &vがエラーにならない
a = v
fmt.Println(a.Abs())
}
type MyFloat float64
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
type Vertex struct {
X, Y float64
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
インターフェイスの実装は、明示的に宣言する必要はない
implementsは必要ない
なんかダックタイピングみたいだね
インターフェイスの値は、値と具体的な型のタプルのように考えることができる
code:interface
(vlaue, type)
インターフェイスの値は、特定の規定になる具体的な型の値を保持する
インターフェイスの値のメソッドを呼び出すと、その基底型の同じ名前のメソッドが実行される
code:go
package main
import (
"fmt"
"math"
)
type I interface {
M()
}
type T struct {
S string
}
func(t *T) M() {
fmt.Println(t.S)
}
type F float64
func (f F) M() {
fmt.Println(f)
}
func main() {
var i I
i = &T{"Hello"}
describe(i)
i.M()
i = F(math.Pi)
describe(i)
i.M()
}
func describe(i I) {
fmt.Println("F(%v, %T)\n", i, i)
}
インターフェイス自体の中にある具体的な値がnilの場合、メソッドはnilをレシーバとして呼び出される
code:go
type I interface {
M()
}
type T struct {
S string
}
func (t *T) M() {
if t == nil {
fmt.Println("<nil>")
return
}
fmt.Println(t.S)
}
func main() {
...
}
Nil Interface values
nilインターフェイスの値は、値も具体的な型も保持しない
呼び出すと具体的なメソッドを示す型がインターフェイスのタプル内に存在しないため、nilインターフェイスのメソッドを呼び出すとランタイムエラーになる
code:go
type I interface {
M()
}
func main() {
var i I
// causes an error
i.M()
}
The empty interface
0個のメソッドを指定されたインターフェイス型は、からのインターフェイスと呼ばれる
code:go
interface{}
空のインターフェイスは、任意の型の値を保持できる
anyみたいな使い方になりそう
code:go
func main() {
var i interface{}
i = 42
i = "Hello"
}
Type Assertions
型アサーションは、インターフェイスの値の基になる具体的な値を利用する手段を提供する
code:go
t := i.(T)
このコードは、インターフェイスの値iが具体的な型Tを保持し、基になるTの値を変数tに代入することを表す
iがTを保持していない場合、このコードはpanicを引き起こす
型のテストもできる
code:go
t, ok := i.(T)
iがTを保持していればtは基になる値になり、okはtrueになる
そうでなければ、okは falseとなり、tは型Tのゼロ値になりpanicは起きない
Type Switches
型Switchは型によって分岐させることができる
code:go
switch v := i.(type) {
case T:
// here v has type T
case S:
// here v has type S
default:
// no match; here v has the same type as i
}
Stringers
fmtパッケージのインターフェイス
code:go
type Stringer interface {
String() string
}
これを実装しておけば、Printlnできる
code:go
package main
import "fmt"
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}
func main() {
a := Person{"Arthur Dent", 42}
z := Person{"Zaphod Beeblebrox", 9001}
fmt.Println(a, z)
}
Errors
Goのプログラムはエラーの状態をerror値で表現する
error型は組み込みインターフェイス
code:go
type error interface {
Error() string
}
Readers
ioパッケージはio.Readerインターフェイスを規定している
Images
imageパッケージはImageインターフェイスを規定している
code:go
package image
type Image interface {
ColorModel() color.Model
Bounds() Rectangle
At(x, y int) color.Color
}
Goroutine
軽量なスレッド
go f(x, y, z)だけで使用できる
code:go
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
// 実行するときにgoをつける
go say("world")
say("hello")
}
Channels
Channel型は、チャネルオペレータの<-を用いて値の送受信ができる通り道
code:go
ch <- v // v をチャネル ch へ送信する
v := <-ch // ch から受信した変数を v へ割り当てる
データは矢印の方向に流れる
マップとスライスのように、チャネルは以下のようにmakeで作る
code:go
ch := make(chan int)
code:go
package main
import "fmt"
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // send sum to c
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x+y)
}
Buffered Channels
チャネルは、バッファとして使える
バッファを持つチャネルを初期化するにはmakeの二つ目の引数にバッファの長さを与える
code:go
ch := make(chan int, 100)
バッファが詰まると、チャネルへの送信をブロックする
バッファが空の時は、チャネルからの受信をブロックする
ちなみにブロックが解消されないコードを実行するとdeadlock errorになる
code:bash
1
fatal error: all goroutines are asleep - deadlock!
main.main()
/tmp/sandbox2998655081/prog.go:9 +0xb3
Range and Close
チャネルはcloseできる
closeすると、それ以上データが送信されないことをチャネルに伝えられる
チャネルの受信時に2個目のパラメータを割り当てることで、受信側はそのチャネルがcloseされていることを認識できる
code:go
v, ok := <-ch
チャネルが閉じているか受信する値がない場合、okの変数はfalseになる
for i := range cは、チャネルcがcloseされるまでループを続ける
注意: 送り手のチャネルだけをcloseしてください。受け手はcloseしてはいけません。 もしcloseしたチャネルへ送信すると、パニック( panic )します。
もう一つ注意: チャネルは、ファイルとは異なり、通常は、closeする必要はありません。 closeするのは、これ以上値が来ないことを受け手が知る必要があるときにだけです。 例えば、 range ループを終了するという場合です。
code:go
package main
import (
"fmt"
)
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c)
}
func main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
for i := range c {
fmt.Println(i)
}
}
Select
goroutineを複数の通信操作で待たせる
複数あるcaseのいずれかが準備できるようになるまでブロックし、準備ができたcaseを実行する
もし、複数のcaseの準備ができていた場合、caseはランダムに選択される
code:go
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
// cに値が来たら計算を実行し、quit(終了通知用のチャネル)が通知されたらreturnする
// 値さえ受け取れればいい場合は、変数は省略してもOK
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
Default Selection
defaultは、どのcaseも準備ができていないときに実行される
code:go
package main
import (
"fmt"
"time"
)
func main() {
tick := time.Tick(100 * time.Millisecond)
boom := time.After(500 * time.Millisecond)
for {
select {
case <-tick:
fmt.Println("tick.")
case <-boom:
fmt.Println("BOOM!")
return
default:
fmt.Println(" .")
time.Sleep(50 * time.Millisecond)
}
}
}
sync.Mutex
排他制御
一度に1つのgoroutineのみが変数にアクセスできるようにするための仕組み
Goの標準ライブラリは、排他制御をsync.Mutexと次の二つのメソッドで提供する
Lock
Unlock
deferはmutexがunlockされるまで待つ仕組みを持つ
code:go
package main
import (
"fmt"
"sync"
"time"
)
// SafeCounter is safe to use concurrently.
type SafeCounter struct {
mu sync.Mutex
}
// Inc increments the counter for the given key.
func (c *SafeCounter) Inc(key string) {
c.mu.Lock()
// Lock so only one goroutine at a time can access the map c.v.
c.mu.Unlock()
}
// Value returns the current value of the counter for the given key.
func (c *SafeCounter) Value(key string) int {
c.mu.Lock()
// Lock so only one goroutine at a time can access the map c.v.
defer c.mu.Unlock()
}
func main() {
c := SafeCounter{v: make(mapstringint)} for i := 0; i < 1000; i++ {
go c.Inc("somekey")
}
time.Sleep(time.Second)
fmt.Println(c.Value("somekey"))
}