Goアウトプット #03 【関数】

# 関数

- Go言語の関数は複数の値を返すことができる
- 関数の中には名称のない匿名関数を作成することができる
- `main()`関数（エントリーポイント）<br>
└引数、戻り値を返さない<br>
└基本的にプロジェクトに1つで、パッケージはmainになる
- funcキーワードで開始し、関数名、引数（省略可）、戻り値（省略可）を宣言

## 基本的な関数の文法

```go
func main() {
	result := pls(10, 10)
	fmt.Printf("result=%d \n", result)
}

func pls(x int, y int) int {
	return x + y
}

// -> result=20
```

## 多値を返す関数

- Go言語は関数の戻り値を多値で返すことができる

### 戻り値の文法（戻り値の型指定のみ）

```go
func 関数名(引数名 型[, .....])(戻り値の型[, ....]){
	処理
	return 戻り値
}
```

```go
func main() {
	add, sub := calc(10, 20)
	fmt.Println(add, sub)
}

func calc(x int, y int) (int, int) {
	add := x + y
	sub := x - y
	return add, sub
}

// -> 30 -10
```

### 戻り値の文法（戻り値を変数に代入する）

- 「名前付き戻り値」ともいう。

```go
func 関数名(引数名 型[, .....])(戻り値の変数名 戻り値の型[, ....]){
	処理
	return 戻り値
}
```

```go
func main() {
	add, sub := calc(10, 20)
	fmt.Println(add, sub)
}

func calc(x int, y int) (add int, sub int) {
	add = x + y
	sub = x - y
	return
}

// -> 30 -10
```

## 可変長引数

- 引数の型名の前に`...`を入れると、一度に複数の値を渡すことができる。<br>
└関数呼び出し時に暗黙的なスライスが作成される。

```go
func 関数名(引数名...型){
	処理
}
```

```go
func main() {
	sum := CalcSum(100, 200, 300, 400)
	avg := CalcAvg(100, 200, 300, 400)
	fmt.Printf("sum=%d, avg=%d \n", sum, avg)
}

func CalcSum(values ...int) (sum int) {
	for _, value := range values {
		sum += value
	}
	return
}

func CalcAvg(values ...int) (avg int) {
	for _, value := range values {
		avg += value
	}
	avg = avg / cap(values)
	return
}

// -> sum=1000, avg=250
```

## 匿名関数（関数リテラル）

- 関数やメソッド内に作成した関数で、名称を持たない<br>
└すぐに呼び出す場合は、関数の最後に`()`をつける。

### 変数に代入して利用する場合

```go
func main() {
	// 引数の値を表示する匿名関数を変数functionに代入する
	function := func(result int) {
		// 引数として受け取った加算結果を出力する
		fmt.Printf("答え=%d \n", result)
	}
	// 加算結果を匿名関数の引数として入力する
	function(100 + 200)
}

// -> 答え=300
```

### 直ぐに呼び出す場合

※遅延実行で使用する（defer）

```go
func main() {
	// 引数の値を表示する匿名関数を直ぐに実行する
	func(result int) {
		fmt.Printf("答え=%d \n", result)
	}(100 + 200)
}
```

## 関数型

- 関数を変数に代入するときに使用する

```go
// 加算と減算を戻り値として返す関数
func CalcA(x int, y int) (int, int) {
	add := x + y
	sub := x - y
	return add, sub
}

// 多値の合計を戻り値として返す関数
func CalcSum(values ...int) (sum int) {
	for _, value := range values {
		sum += value
	}
	return
}

// 関数型の宣言
type CalcFuncType1 func(int, int) (int, int)
type CalcFuncType2 func(...int) int

func main() {
	var func1 CalcFuncType1 = CalcA
	add, sub := func1(10, 20)
	fmt.Printf("add=%d, sub=%d \n", add, sub)

	// 別の変数を使用すれば、同じ関数型を使用することができる
	var func2 CalcFuncType1 = CalcA
	add, sub = func2(20, 30)
	fmt.Printf("add=%d, sub=%d \n", add, sub)

	var func3 CalcFuncType2 = CalcSum
	sum := func3(100, 200, 300, 400)
	fmt.Printf("sum=%d \n", sum)
}

// -> add=30, sub=-10 
//    add=50, sub=-10 
//    sum=1000
```

## 遅延実行

- 関数呼び出しにキーワード`defer`をつけると、遅延実行することができる。<br>
└遅延実行は、`defer`を記述した関数の処理が全て終わったときに実行される<br>
└パニックが発生しても遅延実行の関数は必ず実行される<br>
└関数内喉の場所に記述しても、最後に実行される<br>
└ループ処理内で遅延実行されると、実行を置いておくことができ、LIFO形式で実行される

```go
func Message(msg string) {
	fmt.Println(msg)
}

func main() {
	defer Message("message1")
	fmt.Println("実行")
	defer Message("message2")
}
// LIFO形式で実行される

// -> 実行
//    message2
//    message1
```

### ループ内で実行

```go
func message(msg string) {
	fmt.Println(msg)
}

func main() {
	for i := 0; i < 5; i++ {
		fmt.Println("実行 i=", i)
		defer message(fmt.Sprintf("メッセージ%d", i))
	}
}

// -> 実行 i= 0
//    実行 i= 1
//    実行 i= 2
//    実行 i= 3
//    実行 i= 4
//    メッセージ4
//    メッセージ3
//    メッセージ2
//    メッセージ1
//    メッセージ0
```