0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-09-14

###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())
}

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

[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

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

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】
・復習
"&変数"でアドレスを取得
"*変数"でアドレスを元に参照先の値を取得

//関数1
// 引数に変数を指定しているのに、ポインタをいれるとエラー
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))
}

//関数2
//関数の引数にポインタを指定したら、関数の引数は変数でもポインタでも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))
}
0
0
0

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
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?