プログラミング言語 Go を読みながらメモ。
第一章 : https://qiita.com/Nabetani/items/077c6b4d3d1ce0a2c3fd
第二章 : https://qiita.com/Nabetani/items/d053304698dfa3601116
第三章 : https://qiita.com/Nabetani/items/2fd9c372fcd8383955a5
で。
配列
配列の要素が比較可能であればその配列型も比較可能
とあるので、不等号も試してみた。
package main
import (
"fmt"
)
func main() {
a := [2]int{1, 2}
b := [2]int{3, 4}
fmt.Println(a < b) // invalid operation: a < b (operator < not defined on array)
}
駄目なのか。
スライス
配列と異なり、スライスは比較可能ではありません
そうなのか。何のために比較不能にしたんだろうか...と思ったら、98ページに書いてあった。
何故そうなのかが書いてるのは素晴らしいことだと思う。
とまれ。エラーを出してみる:
package main
import (
"fmt"
)
func main() {
a := []int{1, 2}
b := []int{3, 4}
fmt.Println(a == b) // invalid operation: a == b (slice can only be compared to nil)
}
なるほど。
しかし、97ページの equal のような関数を見ると、C++ のようなテンプレートが欲しくなるね。我慢我慢。
スライスの append
package main
import (
"fmt"
)
func main() {
a := []int{1, 2, 3}
b := a[:1]
fmt.Printf("a=%v, b=%v, cap(b)=%v\n", a, b, cap(b))
for i := 1; i <= 4; i++ {
b = append(b, i*100)
b[0] = i * 111
fmt.Printf("a=%v, b=%v, cap(b)=%v\n", a, b, cap(b))
}
}
とやると、
a=[1 2 3], b=[1], cap(b)=3
a=[111 100 3], b=[111 100], cap(b)=3
a=[222 100 200], b=[222 100 200], cap(b)=3
a=[222 100 200], b=[333 100 200 300], cap(b)=6
a=[222 100 200], b=[444 100 200 300 400], cap(b)=6
と出力される。
まあわかっていればそういうもんかとも思うけど、わかりにくいバグの原因になりがちな気もする。気をつけよう。
あと。範囲外へのアクセスも面白い。
package main
import (
"fmt"
)
func main() {
a := []int{1, 2, 3, 4, 5, 6, 7}
b := a[:1]
fmt.Printf("len(b)=%v b[4:5]=%v\n", len(b), b[4:5])
//=> len(b)=1 b[4:5]=[5]
fmt.Println(b[4]) // panic: runtime error: index out of range
}
スライスを取るのは合法だけど、値を取りに行くのは違法。なんでだろう。
#マップ
マップの要素のアドレスは取れないらしい。では、スライスはどうか。
package main
import (
"fmt"
)
func main() {
a := []int{1, 2, 3, 4, 5, 6, 7}
fmt.Printf("&a[1]=%p\n", &a[1])
}
取れる。一安心。
map 型の要素アクセスの二番目の返戻値にその要素が存在するかどうかが入っているというのは面白いアイディアだと思った。
package main
import (
"fmt"
)
func main() {
a := map[string]int{"alice": 1, "bob": 2}
fmt.Printf("a=%v\n", a)
bv, bok := a["bob"]
fmt.Printf(`a["bob"] = %v, %v`+"\n", bv, bok) //=>a["bob"] = 2, true
cv, cok := a["charlie"]
fmt.Printf(`a["charlie"] = %v, %v`+"\n", cv, cok) //=>a["charlie"] = 0, false
}
nil map でも、要素へのアクセスが出来るのは不思議な気もする。以下の通り:
package main
import (
"fmt"
)
func main() {
var a map[string]int
fmt.Printf("a=%[1]v %[1]T\n", a) //=> a=map[] map[string]int
bv, bok := a["bob"]
fmt.Printf(`a["bob"] = %v, %v`+"\n", bv, bok) //=>a["bob"] = 0, false
}
一方。
スライスは、nil だと 長さは取れるものの要素にアクセスを試みると panic になる。以下の通り:
package main
import (
"fmt"
)
func main() {
var slice []int
fmt.Println(len(slice)) //=> 0
fmt.Println(slice[0]) //=> panic: runtime error: index out of range
}
構造体
構造体のポインタからメンバを参照する(C言語で言う ->
演算子を使う場面)で、単なるピリオドが使えるらしい。便利なような、紛らわしいような。
じゃあポインタのポインタならどうなる?
package main
import "fmt"
type someType struct {
ID int
}
func main() {
t := someType{123}
p := &t
q := &p
fmt.Println(q)
fmt.Println(t.ID) // もちろん okay
fmt.Println(p.ID) // okay
fmt.Println(q.ID) // q.ID undefined (type **SomeType has no field or method ID)
}
駄目みたい。まあそうか。
pointerToPoint := &Point{1,2}
が面白い。C言語脳だとちょっと混乱する。
無名フィールド
これはクラスの継承っぽい。しかも多重継承。
継承っぽいけど、無名のメンバーがあるだけなので is_a? の関係はなくていい。面白い。
冗長な記述を許すことで同名のフィールドが合っても大丈夫なようにしているところも C++ の多重継承っぽい動き。
#JSON
Marshal
json.MarshalIndent(略)
の、第一返戻値が string
ではなく []byte
なのは何故だろう。
そういえば C# もそうだったような気が。
Unmarshal
package main
import (
"encoding/json"
"fmt"
)
type Point struct {
X int
Y int
}
type Circle struct {
Point
Radius int
}
type Wheel struct {
Circle
Spoke int
}
func main() {
js := `{
"X": 6,
"Y": 7,
"Spoke": 0,
"NoSuchField": 1234
}`
var w Wheel
var data []byte = []byte(js)
json.Unmarshal(data, &w)
fmt.Printf("%#v\n", w)
//=> main.Wheel{Circle:main.Circle{Point:main.Point{X:6, Y:7}, Radius:0}, Spoke:0}
}
構造体のメンバにあって JSON にない場合はゼロ初期化。
構造体のメンバになくて JSON にある場合は無視。
わかりやすくて好ましい。
type Point struct {
X int `json:"xx"`
Y int `json:"yy"`
}
という文法は正直意外。
区切り文字なしで注釈を文字列として書くのか。
テキストテンプレート
不思議な機能が。ruby の ERB みたいなものか。
この本のテンプレートのソースコードは難しく、理解できなかった。
仕方ないので https://golang.org/pkg/text/template/ を見た。
上記サイトに有るソースをそのまま(フォーマットだけ整えて)書いておくと:
package main
import (
"html/template"
"os"
)
func main() {
type Inventory struct {
Material string
Count uint
}
sweaters := Inventory{"wool", 17}
tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}}")
if err != nil {
panic(err)
}
err = tmpl.Execute(os.Stdout, sweaters)
if err != nil {
panic(err)
}
}
これならわかる。
template 内の range
を使った例は、こんな感じ:
package main
import (
"os"
"text/template"
)
func main() {
type Inventory struct {
Material string
Count uint
}
sweaters := []Inventory{Inventory{"<wool>", 17}, Inventory{"<cotton>", 20}}
tmpl, err := template.New("test").Parse(`Sweaters :
{{range .}}{{.Count}} items are made of {{.Material}}
{{end}}`)
if err != nil {
panic(err)
}
err = tmpl.Execute(os.Stdout, sweaters)
if err != nil {
panic(err)
}
}
range this
とか range self
とか書きたくなる場所には range .
と書くらしい。
出力は
Sweaters :
17 items are made of <wool>
20 items are made of <cotton>
template 用の言語が Go そのものとは別の文法になっているところがダサいし覚えるのが大変だけど、その分安全なんだと思う。まあ安全のほうが大事だよね。
HTML テンプレート
エスケープの仕方が違うだけで、普通の template とほぼ同じみたい。
package main
import (
"html/template"
"os"
)
func main() {
type Inventory struct {
Material string
Count uint
Hoge template.HTML // エスケープされない
}
sweaters := []Inventory{
Inventory{"<wool>", 17, "<hr/>"},
Inventory{"<cotton>", 20, "<hr noshade/>"}, // ここにコンマが必要なのは何故だろう
}
tmpl, err := template.New("test").Parse(
`<h1>Sweaters</h1>
<table><tr><th>material</th><th>count</th><th>hoge</th></tr>
{{range .}}<tr>
<td> {{.Material}}</td>
<td>{{.Count}}</td>
<td>{{.Hoge}}</td>
</tr>
{{end}}</table>
`)
if err != nil {
panic(err)
}
err = tmpl.Execute(os.Stdout, sweaters)
if err != nil {
panic(err)
}
}
出力は、こんな感じ:
<h1>Sweaters</h1>
<table><tr><th>material</th><th>count</th><th>hoge</th></tr>
<tr>
<td> <wool></td>
<td>17</td>
<td><hr/></td>
</tr>
<tr>
<td> <cotton></td>
<td>20</td>
<td><hr noshade/></td>
</tr>
</table>
第四章はこれで終り。
続いて楽しそうな第五章。
https://qiita.com/Nabetani/items/4b785f1c9b0b26d48475