コミュ障だから明日が僕らをよんだって返事もろくにしなかった

何かを創る人に憧れたからブログをはじめたんだと思うよ

Goツアーのおもいで Part 2

むかしのはなし

これは10年ぐらい昔の話になるんですけど、僕は川で溺れたことがあるんですよ。その時に、もう死ぬかもしれないってなったんですけど、不思議なことに助かりましてね。あの時、走馬灯みたいなもの見えて本当にダメかと思ったのです。人間なにがおこるか分からないものですね……。

という作り話はどうでもいいですね。

Go go Golang!

さてさて、そうしたわけで前回のGo言語の話の続きやっていきましょう。

前回記事
inujini.hatenablog.com


今回はその続きである。「Methods and interfaces」からやっていきます。やっぱり基本文法だけだと美しいコード(当人比)が書けない気がしたのでやっていきます。基本的に写経改変みたいな感じでやっていきます。写経するだけだとつまらないのですが、Goツアーよくわからないことに実習問題までついてたりするのまあいいでしょ。


1. Method
型にメソッド( method )を定義することができるらしいです。JavaScript でも似たようなことやってるの見たな…。

type Vertex struct {
	X, Y float64
}
func (v Vertex) Adds() float64 {
	return v.X+v.Y
}
func main() {
	v := Vertex{3, 4}
	fmt.Println(v.Adds()) // 7
}

例文にあったMathはここの文法理解に直接関係ないので削除。

2. Methods are functions
通常の関数としての書き方。

type Vertex struct {
	X, Y float64
}
func Adds(v Vertex) float64 {
	return v.X + v.Y
}
func main() {
	v := Vertex{3, 4}
	fmt.Println(Adds(v))
}

3. Methods continued
structの型だけではなく、任意の型(type)にもメソッドを宣言できるようです。

4. Pointer receivers
ポインタレシーバでメソッドを宣言できる。

type Vertex struct {
	X, Y float64
}
func (v Vertex) Adds() float64 {
	return 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.Adds())
}

5. Pointers and functions
6. Methods and pointer indirection
7. Methods and pointer indirection (2)
用途として、メソッド内の構造体の書き換えする際にポインタレシーバがいるっぽい。v.Scale(5)(&v).Scale(5) のように解釈するからこうなるらしい。

8. Choosing a value or pointer receiver
ポインタレシーバを使う理由
① メソッドがレシーバが指す先の変数を変更するため
② メソッドの呼び出し毎に変数のコピーを避けるため

9. Interfaces
interface(インタフェース)型は、メソッドのシグニチャの集まりで定義するそうです。

type Abser interface {
	Abs() float64
}

一人でなんか扱っていると使うことなくスルーしてしまう機能。

10. Interfaces are implemented implicitly
これで例文提示されるの見づらすぎる……。implementsは不要らしいです。ここまでやられるとJavaの方が見やすい気がする。

type I interface { // インタフェース型の宣言
	M()
}
type T struct { // 構造体型の宣言
	S string
}
func (t T) M() {
	fmt.Println(t.S)
}
func main() {
	var i I = T{"hello"}
	i.M()
}

11. Interface values
インターフェースの値は(value, type)で捉えるといいらしい。

12. Interface values with nil underlying values
13. Nil interface values
インターフェースnilの扱い編。インターフェース自体の中にある具体的な値が nil の場合、メソッドは nil として処理されるそうです。そしてnil インターフェースの値は、値も具体的な型も保持しないとのことです。これは深く踏み込むとヤバそうな感じがする。とりあえずそんなメソッド呼ぶと以下のように怒られるようにはなってるっぽい。

panic: runtime error: invalid memory address or nil pointer dereference

14. The empty interface
空インタフェースの作り方。interface{}でつくれて、どんな型でも使えるようにできているらしい。

func main() {
	var i interface{}
	describe(i)
}
func describe(i interface{}) {
	fmt.Printf("(%v, %T)\n", i, i) // (<nil>, <nil>)
}

15. Type assertions
例文だけだと空インタフェースの確認手段ぐらいの認識。okを使うとokかどうか返ってくる。

	s := i.(string)
	fmt.Println(s)

	s, ok := i.(string)
	fmt.Println(s, ok)

	f = i.(float64) // panic: interface conversion: interface {} is string, not float64
	fmt.Println(f)

16. Type switches
型switchとは……。C#で似たようなやつ見た気がする。型で比較できるらしい。応用できれば使い勝手は広がりそうだけど、イマイチいい例が思い浮かばない。

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

17. Stringers
ストリンガー、もっともよくつかわれるInterfaceの一つらしい。

type Stringer interface {
    String() string
}

これだけ見てもいまいちよくわかんないけど、次実習だしそれ解いてから考える。

18.Exercise: Stringers
突然の実習問題!!

import "fmt"

type IPAddr [4]byte

// TODO: Add a "String() string" method to IPAddr.
func (ad IPAddr) String() string {
	return fmt.Sprintf("%d.%d.%d.%d", ad[0], ad[1], ad[2], ad[3])
}

func main() {
	hosts := map[string]IPAddr{
		"loopback":  {127, 0, 0, 1},
		"googleDNS": {8, 8, 8, 8},
	}
	for name, ip := range hosts {
		fmt.Printf("%v: %v\n", name, ip)
	}
}

こうなった。配列をループで取得するなどの方法もあるらしいけど、別にいらないのではと至ったのでごり押た。

19. Errors
エラーの書き方。

i, err := strconv.Atoi("42") 
if err != nil {
    fmt.Printf("couldn't convert number: %v\n", err) // エラーメッセージ
    return
}
fmt.Println("Converted integer:", i) 

20. Exercise: Errors
突然の実習問題!!
エラーの実装。

package main
import (
	"fmt"
//	"math"
)

type ErrNegativeSqrt float64

func (e ErrNegativeSqrt) Error() string {
    return fmt.Sprintf("cannot Sqrt nagative number: %g", float64(e))
}

func Sqrt(x float64) (float64, error) {
	if x < 0{
		return 0, ErrNegativeSqrt(x)
	}

	var z float64 = 1
	for i := 0; i < 10; i++ {
		z = z - ((z*z - x) / (2 * z))
	}
	return z, nil
}

func main() {
    fmt.Println(Sqrt(2))
    fmt.Println(Sqrt(-2))
}

Math使わない結果になった……。前回のやり方がやってしまったかとか思ったけど、今回はエラー実装だしいいやってなってこのまあ続行。多分、こうだと思う。

21. Readers
io.Readerについて、ioパッケージの簡単なお話。使い方とか分からんなーとか思ってたら次の項目が練習問題だったからそれ解く。

22. Exercise: Readers
突然の実習問題!!
ASCII文字 'A' の無限ストリームを出力する問題。

package main
import "golang.org/x/tour/reader"
type MyReader struct{}

// TODO: Add a Read([]byte) (int, error) method to MyReader.
func (mr MyReader) Read(rb []byte) (n int, e error) {
    for n, e = 0, nil; n < len(rb); n++ {
        rb[n] = 'A'
    }
    return len(rb), nil
}

func main() {
	reader.Validate(MyReader{})
}

多分、こんな感じ。

23. Exercise: rot13Reader
突然の実習問題!!
より実践っぽい実装。ROT13をつくれ問題。ROT13って何さって調べたら13文字ずらしのシーザー暗号のことだった。

package main
import (
	"io"
	"os"
	"strings"
)

type rot13Reader struct {
	r io.Reader
}

func (a *rot13Reader) Read(rb []byte) (n int, e error) {
    n, e = a.r.Read(rb)
    if e == nil {
		for i ,v := range rb {
			if ('A' <= v && v <= 'M') || ('a' <= v && v <= 'm'){
				rb[i] += 13
			} else if ('N' <= v && v <= 'Z') || ('n' <= v && v <= 'z') {
				rb[i] -= 13
			}
		}
	}
    return
}

func main() {
	s := strings.NewReader("Lbh penpxrq gur pbqr!")
	r := rot13Reader{s}
	io.Copy(os.Stdout, &r)
}

こんな感じになりました。実行するとYou cracked the code!とかでます。

24, Images
Imageパッケージについて、次元画像について何かするのに使えるっぽいです。

25. Exercise: Images
突然の実習問題!!
昔つくった画像ジェネレーターをImageで作ってみよう系問題。ColoeModelBoundsAtがあればいいっぽいね。

package main
import "golang.org/x/tour/pic"
import(
	"image"
	"image/color"
)
type Image struct{
	w,h int
}
// ColorModel
func (img Image) ColorModel() color.Model {
    return color.RGBAModel
}
// Bounds
func (img Image) Bounds() image.Rectangle {
    return image.Rect(0, 0, img.w, img.h)
}
// At
func (img Image) At(x, y int) color.Color {
    v := uint8(x^y)
    return color.RGBA{v, v, 255, 255}
}

func main() {
	m := Image{255,255}
	pic.ShowImage(m)
}

こんなんでしょうか?元から用意されている部分を結構いじってるからあんまりいい書き方な気がしない。まあ動くいいしいいか……。

おわりに

といった感じ少し深い内容に突っ込んでみました。サクッと終わるだろうとか慢心したら案の定サクッと終わらなかった。Goっぽい書き方で書いていきたいとか悩みながらやってたんだけど、そもそも僕はGoっぽい書き方を知らなかった。Gotoつかえばいいんですかね?

さて、また文章が長くなってしまったので続きは次のパートに書こうと思います。次回はGo言語の真骨頂並行処理だ。たのしみー。まあ、次回いつ投稿するか分かんないんですけどね……。