A Tour of Go メモ　【5】 三日目

###A Tour of Go

#Methods

### 関数
| 引数 | func ScaleFunc(v Vertex, i int) | func ScaleFunc(v *Vertex, i int) |
|:-:|:-:|:-:|
|  変数(n) | ◯ | ×  |
| ポインタ(*n) | ◯ | × |
| アドレス(&p) | × | ◯ |

###メソッド

| レシーバ|　func (v Vertex)）　| func (v *Vertex) |
|:-:|:-:|:-:|:-:|
| 変数(n) |   ○|  ◯ |  ◯ |
|  アドレス(&p) |  ◯ |  ◯ |

struct と type　について
[Goを学びたての人が誤解しがちなtypeと構造体について](https://qiita.com/tenntenn/items/45c568d43e950292bc31 #)

```
type Vertex struct {
	X, Y float64
}

type Name struct {
    first, last string
}

func(n Name) Greet() string {
    //変数.値でメソッド内で構造体の値を使うことができる
    return "I'm " + n.first + " " + n.last
}

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

func main() {
	v := Vertex{3, 4}
	fmt.Println(v.Abs())
	n := Name{"first", "last"}
	fmt.Println(n.Greed())
}

> 5
> I'm first last

#通常の関数としてもかける
type Name struct {
    first, last string
}

func Greet(n Name) string {
    return "I'm " + n.first + " " + n.last
}

func main() {
   n := Name{"first", "last"}
   fmt.Println(Greet(n))
}

> I'm first last

*任意の型にメソッドを宣言できる

type MyFloat float64

func (f MyFloat) Abs() float64 {
	if f < 0 {
		return float64(-f)
	}
	return float64(f)
}

func main() {
	f := MyFloat(-math.Sqrt2)
	fmt.Println(f.Abs())
}
```

#ポインタレシーバ【１】
構造体も引数にされた関数内での値はコピーされたものなので、関数内の変更は関数外の元の構造体に反映されない。ポインタを引数に指定すれば、関数内の変更も元の構造体に反映される

[https://www.yoheim.net/blog.php?q=20170902](https://www.yoheim.net/blog.php?q=20170902 #)
[ポインタについて学ぶ（基本的なところ）](https://www.yoheim.net/blog.php?q=20170901 #)
> 関数の引数に値を渡した場合には、関数が実行される際に値がコピーされ、参照先のアドレスが変わります。
> そのため、以下のように関数内で値を変更したとしても、呼び出し元の変数には値が反映されません。

####ポインタレシーバを使う理由


```
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 main() {
	v := Vertex{3, 4}
	fmt.Println(v.X)
	v.Scale(10)
	fmt.Println(v.X)
	fmt.Println(v.Abs())
}

> 50

*ポインタを使わないと関数内の変更が反映されない

func (v Vertex) Scale(f float64) {
    // そもそもここのvは元の構造体と違う別物、値はコピーされる
    // コピーした値を変更し使用しているため、元の構造体の値は変更されない
    // この関数内でのみ、変更された値を使うことができる
	v.X = v.X * f
	v.Y = v.Y * f
}

func main() {
	v := Vertex{3, 4}
	v.Scale(10)
    //関数外なので、XとYの値は最初に宣言した3,4のままである
	fmt.Println(v.Abs())
}

> 5

```

#ポインタレシーバ【２】

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

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

func main() {
	v := Vertex{3, 4}
     //  ＆vはvのアドレスであるため、エラーなる
     // Scale(v, 10)ならエラーにならないが、vの値の変更は関数内のみ適用される
	Scale(&v, 10)
	fmt.Println(Abs(v))

    //  例えば、 n := &として引数に*nとするとエラーにならないが、
    //しかし同様にポインタレシーバでないので、Vertexの値は変わらない
　　 Scale(*n, 10)
	fmt.Println(Abs(v))
}
//　Scaleメソッドはポインタレシーバではない
// つまりScaleメソッドは、ポインタを引数に取らないためエラーになる
> cannot use &v (type *Vertex) as type Vertex in argument to Scale

//Scale関数内では値は変わるが、関数外では元のVertexの値は変わらない
>5
```

* ポインタレシーバであった場合は、レシーバ（呼び出し元）が変数でもポインタでもok（変数の場合は自動で"&変数"として解釈してくれる）

```
type Vertex struct {
	X, Y float64
}
//*Vertex型(Vertexへのポインタである *Vertex型)
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を"&v"として自動で解釈してくれている）
	v.Scale(2)
	fmt.Println(v)
	ScaleFunc(&v, 10)
	fmt.Println(v)
    //　もちろん、こちらはアドレスを渡しているので、ok
	p := &Vertex{4, 3}
	p.Scale(3)
	fmt.Println(v)
	ScaleFunc(p, 8)
    fmt.Println(v)
	fmt.Println(v, p)
}
```
#ポインタレシーバ【3】
・復習
"&変数"でアドレスを取得
"*変数"でアドレスを元に参照先の値を取得

```
//関数１
//　引数に変数を指定しているのに、ポインタをいれるとエラー
func Scale(v Vertex, f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}

func main() {
    v:= Vertex{4, 3}
    fmt.Println(Scale(&v))
}

//関数２
//関数の引数にポインタを指定したら、関数の引数は変数でもポインタでもOK
func Scale(v *Vertex, f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}

func main() {
    n = Vertex{5, 4}
    fmt.Println(Scale(n))

    v:= &Vertex{4, 3}
    fmt.Println(Scale(v))
}

//メソッド

//メソッドを設定時、レシーバに変数を指定したら、メソッド実行時のレシーバは変数はもちろん、ポインタ変数でもOK
//・・・レシーバ(メソッドの呼び出し元、今回はVertex）
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}
    //レシーバ（呼び出し元）が変数vなのでOK
	fmt.Println(v.Abs())
	fmt.Println(AbsFunc(v))

	p := &Vertex{4, 3}
    // レシーバがポインタ変数(p=&Vertex)でもOK
	fmt.Println(p.Abs())
	fmt.Println(AbsFunc(*p))
}
```