Tour of Go
naked
returnといって、返り値に名前をつけることができ、変数名として扱うことができる。
return
ステートメントで何も書かずに書くことができるが、長い関数内で使うとreadabilityが下がるので短いコード内でのみ使うようにする
func split(sum int) (hoge, fuga int) {
hoge = sum * 4 / 9
fuga = sum - hoge
return
}
定数( const
)は以下のように、:=
を使って省略して書くことはできない
const Pi = 3.14
func main() {
const World = "世界"
fmt.Println("Hello", World)
fmt.Println("Happy", Pi, "Day")
const Truth = true
fmt.Println("Go rules?", Truth)
}
defer
へ渡した関数の実行(ここではfmt.Println
)を、呼び出し元の関数(ここではmain()
)の終わりまで遅延させることができる
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
スライスを作成するのはビルトインの make
関数で可能
func main() {
a := make([]int, 5)
printSlice("a", a)
}
sliceについて
sliceのcapacityの自動増加がよう分からんな
スライスやmapを一つずつ反復処理したいときに、for文にrangeを使う
以下のスライスをrangeで繰り返す場合は、2つの変数を返す。
1つ目はそのスライスのindex番号を、2つ目はそのindexに対応する要素を返す。
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)
}
}
indexだけ欲しい場合、valueだけ欲しい場合は以下のように省略して記述できる
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
func main() {
// indexだけ必要な場合
for index := range pow {
fmt.Printf("%d\n", i)
}
// valueだけ必要な場合
for _, value := range pow {
fmt.Printf("%d\n", value)
}
}
Exercise: Maps
wc.Test
のソースコード
testCases
というstructを要素数を指定せずに宣言している
[]struct {} {}
みたいに構造体の宣言と代入を行なっている(参考:https://www.twihike.dev/docs/golang-primer/structs)
in
というstring
のフィールド名に、want
というmap[string]int
のフィールド値から構成される
例えば、"I am learning Go!"
というin
に対応するwant
はmap[string]int{"I": 1, "am": 1, "learning": 1, "Go!": 1,}
wc.Test(WordCount)
で、WorstCount関数を変数としてTest関数に渡している
Test関数内で、testCasesのrangeでループ処理をしている
testCases (Type : map) のin
フィールドをそれぞれWordCount
関数の引数に渡して、c.wantのlengthとWordCount(c.in)の返り値のlengthが一緒で、c.wantのインデックスkの値とWordCount(c.in)の返り値のインデックスkが一緒の場合、テストがパスされるようになっている
関数内で定義された関数(例えば、関数A内で定義された関数B)のことをクロージャー(closure)という
以下の例では、adder()
関数はクロージャーを返している
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
この場合は、以下のようにpos, negという変数に返り値であるクロージャーを代入することで、pos(1)やpos(2)のように関数として扱うことができる
func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}
以下2つの代入方法は異なる
1つ目はf0
, f1
が同時に更新される
f0, f1 = f1, f0 + f1
2つ目のケースは、f0
の値がf1
に更新された後、f1
の値が計算される
f0 = f1
f1 = f0 + f1
ポインタレシーバ
package main
import (
"fmt"
"math"
)
type XandY struct {
X, Y float64
}
func (v XandY) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
// ポインタレシーバ(*)がある場合、ポインタが指す変数を変更することができる
// ポインタが無い場合は、変数を変更することができない
// XandYの値を変更することはできないため、後続の処理に更新された値が反映されない
func (v *XandY) Scale(f float64) float64 {
v.X = v.X * f
v.Y = v.Y * f
return v.X
}
func main() {
v := XandY{X:3, Y:4}
fmt.Println(v.Scale(10))
fmt.Println(v.Abs())
}
メソッドとレシーバについてのわかりやすい記事
Exercise: Stringers
解答例
package main
import "fmt"
type IPAddr [4]byte
// TODO: Add a "String() string" method to IPAddr.
// IPAddr型にString()メソッドを追加する
func (i IPAddr) String() string {
return fmt.Sprintf("%d.%d.%d.%d", i[0], i[1], i[2], i[3])
}
func main() {
// string型をキーに、IPAddr型を値に持つmap型のhostsという変数を宣言
hosts := map[string]IPAddr{
"loopback": {127, 0, 0, 1},
"googleDNS": {8, 8, 8, 8},
}
// hostsをfor range文でループ処理させる
// この時、key , valueの順番にmapの値が代入される(sliceの場合だとindexとvalue)
for name, ip := range hosts {
fmt.Printf("%v: %v\n", name, ip)
}
}