ENTRANCE

都内でエンジニアやってます。主にRubyとJavaを書いて遊んでる人のブログ。楽しくのんびりとがモットー。

【GO】Golangの勉強を始めたから殴り書きのメモ~その3

つづき。 tour.golang.org

kattsundesu.hatenablog.com

Methods

  • メソッドの定義(レーシーバー)
package main

import (
    "fmt"
    "math"
)

type Vertex struct {
    X, Y float64
}

// Vertex型のレシーバーをもち、float64型の返り値をもつ、Absという名前の関数
func (v Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

type MyFloat float64

// typeでも定義できる
func (f MyFloat) Abs() float64 {
  if f < 0 {
        return float64(-f)
    }
    return float64(f)
}

func main() {
  v := Vertex{3, 4}
  // 呼び出す
  fmt.Println(v.Abs())
  
  // 呼び出す
  f := MyFloat(-math.Sqrt2)
    fmt.Println(f.Abs())
}
  • メソッドの定義(通常)
package main

import (
    "fmt"
    "math"
)

type Vertex struct {
    X, Y float64
}

// Vertex型を引数にもち、float64型を返り値にもつ、Absという名前の関数
func Abs(v Vertex) float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
  v := Vertex{3, 9}
  // 呼び出す
    fmt.Println(Abs(v))
}
  • ポインタレシーバ
package main

import (
    "fmt"
    "math"
)

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 (v Vertex) Scale2(f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
}

func main() {

  v := Vertex{3, 4}
  // ポインタレシーバーを使う
  v.Scale(10)
  // vの値が変わってる
  fmt.Println(v.Abs()) // 50
  fmt.Println(v) // {30 40}
  
  g := Vertex{3, 4}
  // 変数レシーバを使う
  g.Scale2(10)
  // gの値は変わっていない
  fmt.Println(g.Abs()) // 5
  fmt.Println(g) // {3 4}
}
  • ポインタとメソッド
package main

import "fmt"

type Vertex struct {
    X, Y float64
}

func (v *Vertex) Scale(f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
}

func ScaleFunc(v *Vertex, f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
}

func main() {
    v := Vertex{3, 4}
    v.Scale(2)
    // ポインタを引数に渡す
    ScaleFunc(&v, 10)

    p := &Vertex{4, 3}
    p.Scale(3) // = (*p).Scale(3)
    // pはポインタ
    ScaleFunc(p, 8)

    fmt.Println(v, p)
}
  • パート2
import (
    "fmt"
    "math"
)

type Vertex struct {
    X, Y float64
}

func (v Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func AbsFunc(v Vertex) float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
    v := Vertex{3, 4}
    fmt.Println(v.Abs()) // 5
    // 引数はポインタではない
    fmt.Println(AbsFunc(v)) // 5

    p := &Vertex{4, 3}
    fmt.Println(p.Abs()) // = fmt.Println((*p).Abs()) // 5
    // 引数はポインタ
    fmt.Println(AbsFunc(*p)) // 5
}

-

package main

import (
    "fmt"
    "math"
)

type Vertex struct {
    X, Y float64
}

func (v *Vertex) Scale(f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
}

func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
    v := &Vertex{3, 4}
    fmt.Printf("Before scaling: %+v, Abs: %v\n", v, v.Abs()) // Before scaling: &{X:3 Y:4}, Abs: 5
    v.Scale(5)
    fmt.Printf("After scaling: %+v, Abs: %v\n", v, v.Abs()) // After scaling: &{X:15 Y:20}, Abs: 25
}

Interfaces

package main

import "fmt"

// インターフェースの定義
type I interface {
    M()
}

type T struct {
    S string
}

// このメソッドを定義することで、
// 明示的に書かなくても、TはI(インターフェース)を実装していることになる。
func (t T) M() {
    fmt.Println(t.S)
}

func main() {
    var i I = T{"hello"}
    // 呼び出し
    i.M() // hello
}
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) // (&{Hello}, *main.T)
    i.M() // Hello

    i = F(math.Pi)
    describe(i) // (3.141592653589793, main.F)
    i.M() // 3.141592653589793
}

func describe(i I) {
    fmt.Printf("(%v, %T)\n", i, i)
}
package main

import "fmt"

// インターフェース
type I interface {
    M()
}

type T struct {
    S string
}

func (t *T) M() {
    // レジーバーがnilでも例外を引き起こさないようにメソッドを記述するのが一般的
    if t == nil {
        fmt.Println("<nil>")
        return
    }
    fmt.Println(t.S)
}

func main() {
    var i I

    var t *T
    i = t
    // ゼロ値はnil
    describe(i) // (<nil>, *main.T)
    i.M() // <nil>

    i = &T{"hello"}
    describe(i) // (&{hello}, *main.T)
    i.M() // hello
}

func describe(i I) {
    fmt.Printf("(%v, %T)\n", i, i)
}
  • Nil interface values
package main

import "fmt"

type I interface {
    M()
}

func main() {
    // 初期化はしない
    var i I
    // 実行される
    describe(i) // (<nil>, <nil>)
    // ランタイムエラー
    i.M()
}

func describe(i I) {
    fmt.Printf("(%v, %T)\n", i, i)
}
  • The empty interface
package main

import "fmt"

func main() {
    // 空のインターフェース
    var i interface{}
    describe(i) // (<nil>, <nil>)

    i = 42
    describe(i) // (42, int)

    i = "hello"
    describe(i) // (hello, string)
}

func describe(i interface{}) {
    fmt.Printf("(%v, %T)\n", i, i)
}
  • 型の検証
package main

import "fmt"

func main() {
    // インターフェースに元となる値を代入
    var i interface{} = "hello"

    // iがstring型を保持しているか検証
    s := i.(string) // stringでない場合は、panic!
    fmt.Println(s) // hello

    // iがstring型を保持しているか検証(bool値付き)
    s, ok := i.(string) // stringを保持すればTrue,保持しない場合はFalseでpanicは起きない。
    fmt.Println(s, ok) // hello true

    f, ok := i.(float64)
    fmt.Println(f, ok) // 0 false

    f = i.(float64) // panic
    fmt.Println(f)
}
  • Type switches
package main

import "fmt"

func do(i interface{}) {
    switch v := i.(type) {
    case int:
        fmt.Printf("Twice %v is %v\n", v, v*2)
    case string:
        fmt.Printf("%q is %v bytes long\n", v, len(v))
    default:
        fmt.Printf("I don't know about type %T!\n", v)
    }
}

func main() {
    do(21)      // Twice 21 is 42
    do("hello") // "hello" is 5 bytes long
    do(true)    // I don't know about type bool!
}