LoginSignup
0

More than 1 year has passed since last update.

Organization

A Tour of Go メモ ~Methods and interfaces~

はじめに

Go の勉強のため、A Tour of Goに取り組んでいる
今回はMethods and interfaces章について、学んだことを記していく

まとめ記事はこちら

使えそうなスニペット

※筆者は VSCode を使用

meth

method を作成する。すごい便利

func (receiver type) method()  {

}

tyi

type name interface {

}

ページごとの補足

Methods and pointer indirection

struct と*struct の違いを調べておきたい

The empty interface

使いみち

Typescript におけるunknownに近い?

Type assertions

使いみち

var i interface{} = "Hello"
s, ok := i.(int) // no panic
var i interface{} = "Hello"
s := i.(int) // panic

Exercise: Stringers

func (ip IPAddr) String() string {
    return fmt.Sprintf("%v.%v.%v.%v", ip[0], ip[1], ip[2], ip[3])
}

マジックナンバーを使わない場合、こんな感じになりそう

func (ip IPAddr) String() string {
    stringArray := make([]string, 0, len(ip))
    for _, i := range ip {
        stringArray = append(stringArray, fmt.Sprint(i))
    }
    return strings.Join(stringArray, ".")
}

Errors

関数の返り値の型にerrorを指定した場合、return した値のError()メソッドが実行される

Exercise: Errors

naked return ステートメントが便利
error の初期値に nil が勝手に入ってくれる

type ErrNegativeSqrt float64

func (e ErrNegativeSqrt) Error() string {
    return fmt.Sprintf("Error: %v", float64(e))
}

func Sqrt(x float64) (z float64, e error) {
    if x < 0 {
        e = ErrNegativeSqrt(x)
        return
    }
    z = 1
    for i := 0; i < 10; i++ {
        z -= (z*z - x) / (2 * z)
    }
    return
}

func main() {
    fmt.Println(Sqrt(2))  // 1.414213562373095 <nil>
    fmt.Println(Sqrt(-2)) // 0 Error: -2

}

Exercise: Readers

一応バリデーションは通ったが、こんな実装で良いか不明
他の人の回答を見ると、strings.NewReaderを使わずにシンプルに byte スライスにAを挿入していた
それでいいのか……

type MyReader struct{}

func (m MyReader) Error() string {
    return fmt.Sprint("Error!")
}

func (m MyReader) Read(b []byte) (int, error) {
    var a string
    for i := 0; i < len(b); i++ {
        a += "A"
    }
    r := strings.NewReader(a)

    for {
        _, err := r.Read(b)
        if err == io.EOF {
            break
        }
    }
    return len(b), nil
}

Exercise: rot13Reader

実装自体は単純
ただ、変換後の文字列をどう返せばいいのか悩んだ
Readの引数に渡ってくる byte に文字を格納することで、その文字列が返る

また、13 を足すか引くかを元々数字で判断していた(b < 65のように)
しかし、runeを使うことで、直感的に書くことができる
※ただし、これができるのは一部の文字だけ。日本語は NG

func rot(b byte) byte {
    const ROT = 13
    switch {
    case b < 'A':
        return b
    case b < 'N':
        return b + ROT
    case b < 'a':
        return b - ROT
    case b < 'n':
        return b + ROT
    case b <= 'z':
        return b - ROT
    default:
        return b
    }
}

func (r *rot13Reader) Read(b []byte) (n int, err error) {
    n, err = r.r.Read(b)
    if err != io.EOF {
        for i := range b {
            b[i] = rot(b[i])
        }
    }
    return n, err
}

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

Exercise: Images

struct 初期化時に表示計算用の関数fnを与えるようにしてみた

package main

import (
    "fmt"
    "image"
    "image/color"

    "golang.org/x/tour/pic"
)

type MyImage struct {
    x0, y0, x1, y1 int
    fn             func(x, y int) int
}

func (m MyImage) At(x, y int) color.Color {
    v := uint8(m.fn(x, y))
    return color.RGBA{v, v, 255, 255}
}
func (m MyImage) Bounds() image.Rectangle {
    return image.Rect(m.x0, m.y0, m.x1, m.y1)
}
func (m MyImage) ColorModel() color.Model {
    return color.RGBAModel
}

func main() {
    fn := func(x, y int) int { return x - y }
    m := MyImage{0, 0, 100, 100, fn}
    fmt.Println(m.Bounds())
    fmt.Println(m.At(0, 0).RGBA())
    pic.ShowImage(m)
}

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
0