GO学習機能(機能)

GOシリーズ

1. GO 学習の Hello World
2. GO 学習の入門文法
3. GO 学習のスライス操作
4. GO 学習のマップ操作
5. GO 学習の構造操作
6. GO 学習のチャネル (チャネル)
7.マルチスレッドの GO 学習(ゴルーチン)
8. GO学習関数 (Function)
9. GO学習インターフェース (Interface)

序文

会社の現在のタスクからすると、Go Learning するしか方法はありません。業界は難しいですが、スキルは圧倒的ではありませんが、それでも頑張っていきます。
メソッドについては、前の構造に関する章で説明しました。メソッドは実際には関数に似ていますが、メソッドと関数の違いは、関数はどの型にも属さず、メソッドは特定の型に属することです。この文は構造の中でも触れていますが、関数を利用する上で注意すべき点が多く、この記事で詳しく解説していきます。

1. 関数とは何ですか?

  • Go 言語では、関数 (Function) は、特定のタスクまたは操作を実行するための実行可能なコード ブロック (特定の関数を抽出してコード フラグメントを形成する) です。
  • 関数は Go 言語の基本コンポーネントであり、モジュール化と再利用のメカニズムを実現し、コードをより構造化して保守しやすくします。

私が見つけて思いつく機能のリストは次のとおりです (これらに限定されません)。

Go言語の関数の特徴:

  • プロトタイプを宣言する必要はありません
  • 可変パラメータのサポート
  • 複数の戻り値をサポート
  • 名前付き戻りパラメータのサポート
  • 匿名関数とクロージャのサポート
  • 関数も型なので、関数を変数に割り当てることができます
  • 関数を入れ子にすることはできません
  • JAVAのように関数をオーバーロードすることはできません

2. 関数宣言

  • 関数宣言ではキーワードを使用しますfunc
  • 基本的な構文:func 函数名(参数列表) 返回值列表 { 函数体 }
  • 関数名の命名規則: 1. キャメル ケースで名前を付けるのが最善です。名前を参照してください (例: ; addNum(a,b int){}2. 最初の文字は数字にすることはできません。 3. 最初の文字は、このパッケージおよび他のパッケージ ファイルで使用できます) 、パブリックに似ています。例: AddNum(a,b int){}、小文字のイニシャルはプライベートに似ています。例:addNum(a,b int){}
  • パラメータ リストはカンマで区切られ、各パラメータはパラメータ名とタイプで構成されます。次に例を示します。func list(pageNo int, pageSize int) (int, error) { }
  • 複数のパラメータが同じタイプである場合は、前のパラメータ タイプを省略できます。次に例を示します。func list(pageNo, pageSize int) (int, error) { }
  • この構文を使用して...関数の可変パラメータを定義すると、実行中の関数は次のような可変数のパラメータを受け入れます。
  • 戻り値がない場合は省略できます。例:func save(id int, name string) { }
  • 戻り値の場合、括弧は必要ありません (または使用できません)。次に例を示します。func save(id int, name string) int { }
  • 複数の戻り値がある場合は、それらを括弧で囲み、return ステートメントに 1 つずつ対応させます。次に例を示します。func save(id int, name string) (int, string) { ...return 1,'success' }
  • 前述したように、次のような名前付き戻り関数がサポートされています。func divideAndRemainder(a, b int) (quotient, remainder int) { }
  • キーワードを使用してfunc関数を定義します。中括弧で新しい行を始めることはできません

上記のコード:

3. 関数呼び出し

  • 関数の戻り値を受け入れるときに、複数の戻り値を 1 つずつ受け入れる場合は、たとえば次のようになります。count, result := save(1, "张三")
  • 戻り値の 1 つだけを受け入れ、他の戻り値は受け入れない必要がある場合は、アンダースコアを使用して_それを無視できます。次に例を示します。count, _ := save(1, "张三")
  • メイン パッケージが他のパッケージの関数を呼び出す必要がある場合、他のパッケージの関数はパッケージの外部からアクセスできるように定義する必要があり、関数名の最初の文字は大文字になります。たとえば、 func1 パッケージを定義する場合: が必要です。このときパッケージ名に注意するためにfunc SumNum(a, b int) int { }、呼び出すにはパッケージ名を使用する必要があります。例:s := func1.SumNum(1, 2)

以下の例は、Go 言語で一般的に使用される関数定義の例です。参考にしてください。
例としては、兄と私が 1 つずつ入力し、テストに合格し、実行結果が添付されていますが、目で見るのはまだ少し複雑です。自分で入力したほうが良いですが、問題はありません白の場合は、段階的に入力してさまざまなエラーを解決するしか、成長することができません。

パッケージのパスは次のようになります。
関数パッケージのパス

package func1

import "fmt"

// 定义无参数无返回值函数
func test() {
    
    
	fmt.Println("call test函数")
}

// 定义有参数无返回值函数,此函数私有的,只有内部可调
func addNum(a, b int) {
    
    
	c := a + b
	fmt.Printf("a + b = c %+v\n", c)
}

// 定义有参数有一个返回值函数, 次函数共有的,内部、外部包均可调
func SumNum(a, b int) int {
    
    
	c := a + b
	return c
}

// 定义可变参数函数
func ParamsFunc(params ...string) {
    
    
	for index, item := range params {
    
    
		fmt.Printf("可变参数为 %d:%+v\n", index, item)
	}
}

// 定义有参数有多个返回值函数
func List(pageNo, pageSize int) (int, []string) {
    
    
	fmt.Printf("查询操作...%d, %d", pageNo, pageSize)
	result := []string{
    
    "特斯拉", "广汽", "丰田", "宝马", "奥迪"}
	return 5, result
}

// 定义命名返回函数
func divideAndRemainder(a, b int) (quotient, remainder int) {
    
    
	quotient = a / b
	remainder = a % b
	return // 省略了 return 语句,并且直接返回了命名的返回值变量
}

次の例は、上で定義した関数の呼び出しです。
主に、他のパッケージをインポートするパスはgotest.com/test/src/functionTest/func1です。

package main

import (
	"fmt"

	"gotest.com/test/src/functionTest/func1"
)

func main() {
    
    
	// 调用本包中的 save 函数,接受两个返回值
	count1, result := save(1, "张三")
	fmt.Printf("接受 save 函数的两个返回值 count1:%+v, result: %v\n", count1, result)

	// 调用本包中的 save 函数,接受一个返回值
	count, _ := save(1, "张三")
	fmt.Printf("接受 save 函数的一个返回值 count: %+v\n", count)

	// 调用无返回值函数
	list2(1, 10)

	// 调用 func1 包中的 SumNum 函数
	s := func1.SumNum(1, 2)
	fmt.Printf("调用 func1 包中的 SunNum 函数结果:%+v\n", s)
	
		// 调用可变参数函数
	func1.ParamsFunc("特斯拉", "广汽", "丰田", "宝马", "奥迪")

	// 调用 func1 包中的 List 函数
	totalCount, carBrands := func1.List(1, 10)
	fmt.Printf("调用 func1 包中的 List 函数,查询结果:%+v 条,数据:%v\n", totalCount, carBrands)
}

// 定义有参数有多个返回值函数
func save(id int, name string) (int, string) {
    
    
	fmt.Printf("保存%+v,%v\n", id, name)
	return 1, "success"
}

// 定义有多个参数无返回值函数
func list2(pageNo, pageSize int) {
    
    
	fmt.Println("list 接口")
}

操作結果:

PS D:\workspaceGo\src\functionTest\main> go run funcTest.go
保存1,张三
接受 save 函数的两个返回值 count1:1, result: success
保存1,张三
接受 save 函数的一个返回值 count: 1
list 接口
调用 func1 包中的 SunNum 函数结果:3
可变参数为 0:特斯拉
可变参数为 1:广汽
可变参数为 2:丰田
可变参数为 3:宝马
可变参数为 4:奥迪
查询操作...1, 10调用 func1 包中的 List 函数,查询结果:5 条,数据:[特斯拉 广汽 丰田 宝马 奥迪]

4. 匿名関数

  • Go 言語では、匿名関数、つまり関数名のない関数がサポートされています。
  • 匿名関数を変数に代入できる
  • クロージャである匿名関数を直接呼び出すこともできます。
package main

import "fmt"

func main() {
    
    
	// 定义匿名函数直接调用
	func() {
    
    
		fmt.Println("匿名函数调用!")
	}()
	// 定义匿名函数赋值给变量 hello
	hello := func() {
    
    
		fmt.Println("Hello 函数调用!")
	}
	// 调用匿名函数
	hello()
	// 定义有参数的匿名函数
	sum := func(a, b int) int {
    
    
		return a + b
	}
	fmt.Printf("加法计算:%+v\n", sum(1, 2))
}

操作結果:

PS D:\workspaceGo\src\functionTest\main> go run .\anonymousFunc.go
匿名函数调用!
Hello 函数调用!
加法计算:3

もう少し複雑な例を次に示します。

次の例では、関数を配列 fns、構造体 s、パイプライン fc のメンバーとして保存し、呼び出す関数を取得します。

package main

func main() {
    
    
	// 定义数据,元素类型是一个函数
	fns := [](func(a int) int){
    
    func(a int) int {
    
     return a + 1 }, func(a int) int {
    
     return a + 2 }}
	// 获取数组中的第一个函数调用,传参 10
	for _, fn := range fns {
    
    
		println(fn(10))
	}

	// 定义一个结构体,成员是一个 函数,调用结构体的 函数成员
	s := struct {
    
    
		fn func() string
	}{
    
    
		fn: func() string {
    
     return "Hello World!" },
	}
	println(s.fn())

	// 定义一个管道,发送一个函数,再接受到函数进行调用
	fc := make(chan func() string, 2)
	fc <- func() string {
    
     return "fc: Hello World!" }
	println((<-fc)())
}

操作結果:

PS D:\workspaceGo\src\functionTest\main> go run .\anomymousFunc2.go
11
12
Hello World!
fc: Hello World!

5. 関数のパラメータと戻り値

  • Go では、関数を引数または別の関数の戻り値として渡すことができます。

以下は比較的単純な例で、関数型パラメーター fc を受け取り、匿名関数を返します。

package func1

import "fmt"

func CallFunc(fc func()) func() {
    
    
	fmt.Println("接受到函数 fc, 开始回调!")
	// 返回一个匿名函数
	return func() {
    
    
		fc()
		fmt.Println("call back...")
	}
}

通話コード:

package main

import (
	"fmt"

	"gotest.com/test/src/functionTest/func1"
)

func main() {
    
    
fc := func() {
    
    
		fmt.Println("我是参数 fc 执行!")
	}
	fr := func1.CallFunc(fc)
	fr()
}

操作結果:

PS D:\workspaceGo\src\functionTest\main> go run .\funcTest.go
接受到函数 fc, 开始回调!
我是参数 fc 执行!
call back...

以下は、ChatGPTによって提供される古典的なケースであり、実際のシナリオで関数がパラメーターおよび戻り値としてどのように使用されるかをより深く理解するのに便利です。私がテストした例は ojbk です。

  • 関数はパラメータとして使用されます。
package main

import "fmt"

// 函数类型作为参数
type MathFunc func(int, int) int

// 加法函数
func add(a, b int) int {
    
    
	return a + b
}

// 减法函数
func subtract(a, b int) int {
    
    
	return a - b
}

// 计算函数,接收一个函数类型参数,并执行该函数
func calculate(a, b int, op MathFunc) int {
    
    
	return op(a, b)
}

func main() {
    
    
	// 调用 calculate 函数,传入 add 函数作为参数
	result := calculate(10, 5, add)
	fmt.Println("加法结果:", result)

	// 调用 calculate 函数,传入 subtract 函数作为参数
	result = calculate(10, 5, subtract)
	fmt.Println("减法结果:", result)
}

操作結果:

PS D:\workspaceGo\src\functionTest\main> go run .\gptFunc.go
加法结果: 15
减法结果: 5
  • 戻り値として使用される関数:
package main

import "fmt"

// 返回一个加法函数
func getAddFunc() func(int, int) int {
    
    
	// 返回一个匿名函数,来实现计算
	return func(a, b int) int {
    
    
		return a + b
	}
}

// 返回一个减法函数
func getSubtractFunc() func(int, int) int {
    
    
	return func(a, b int) int {
    
    
		return a - b
	}
}

func main() {
    
    
	// 获取加法函数并调用
	addFunc := getAddFunc()
	result := addFunc(10, 5)
	fmt.Println("加法结果:", result)

	// 获取减法函数并调用
	subtractFunc := getSubtractFunc()
	result = subtractFunc(10, 5)
	fmt.Println("减法结果:", result)
}

結果:

PS D:\workspaceGo\src\functionTest\main> go run .\gptFunc2.go
加法结果: 15
减法结果: 5

6. 遅延実行機能

遅延の特徴:

  • キーワード遅延ユーザー登録遅延呼び出し、例:defer println(i)
  • 登録された遅延呼び出しは返されるまで実行されないため、クローズ、リソース回復などの操作に非常に適しています。
  • defer ステートメントは先入れ後出し方式で実行されます。
  • defer ステートメント内の変数は defer ステートメント内で決定されます。

延期に該当するシナリオ:

  • ストリームを閉じる操作
  • リソースの解放
  • データベース接続の解放
  • 待って…

6.1 先入れ後出しの延期

package main

func main() {
    
    
	arr := [5]int{
    
    1, 2, 3, 4, 5}
	for i := range arr {
    
    
		defer println(i)
	}
}

操作結果:

PS D:\workspaceGo\src\functionTest\main> go run .\deferTest.go
4
3
2
1
0

結果から、最初にループされた遅延は後になるまで実行されないことがわかります。

6.2 遅延クロージャ関数

package main

func main() {
    
    
	arr := [5]int{
    
    1, 2, 3, 4, 5}
	for i := range arr {
    
    
		defer func() {
    
    
			println(i)
		}()
	}
}

操作結果:

PS D:\workspaceGo\src\functionTest\main> go run .\deferTest.go
4
4
4
4
4

なぜすべて 4 に変更されるのでしょうか? ループ本体はクロージャ関数なので宣言直後に実行されますが、関数の宣言時に i 変数が 4 に変更されているため、4 つの無名関数はすべて 4 を出力します。

延期の場合が多いようなので、こちらに移動してください!

7. エラー処理

  • Go 言語のほとんどの関数は追加の戻り値としてエラーを返しますerror。ユーザーは関数が正常に実行されたかどうかを示します。
  • 関数の呼び出しでは、適切に処理できるようにエラーをチェックする必要があることがよくあります。
  • 値を返さない関数の場合、エラー タイプを使用して、関数が正常に実行されたかどうかを示したり、panic例外をトリガーしたりできます。

7.1 戻りパラメータとして error を使用する

この例では、エラー タイプ error を使用して関数が正常に実行されたかどうかを示し、関数が失敗した場合はエラーを返します。

package main

import (
	"errors"
	"fmt"
)

func main() {
    
    
	err := divide(10, 0)
	if err != nil {
    
    
		fmt.Println("发生异常:", err)
	}
}

func divide(a, b int) error {
    
    
	if b == 0 {
    
    
		return errors.New("参数不能为 0")
	}
	return nil
}

操作結果:

PS D:\workspaceGo\src\functionTest\main> go run .\errorFunc.go
发生异常: 参数不能为 0

7.2 パニックを使用して例外をトリガーする

この例では、関数の実行状況を示す例外をトリガーするためにパニックを使用しています。関数内でエラーが発生すると、直接例外がトリガーされ、プログラムの実行が中断されます。
**注意:** プログラムのクラッシュを避けるために、recover() 関数を使用して例外をキャッチして処理します。recover関数はdeferブロック内でのみ使用できるため、例外をキャッチするには main() 関数で defer を使用します。

package main

func main() {
    
    
	defer func() {
    
    
		if r := recover(); r != nil {
    
    
			println("发生异常:", r)
		}
	}()
	divide2(10, 0)
}

func divide2(a, b int) {
    
    
	if b == 0 {
    
    
		panic("参数不能为 0")
	}
}

操作結果:

PS D:\workspaceGo\src\functionTest\main> go run .\panicFunc.go
发生异常: (0xff1920,0x1011638)

8. まとめ

関数は Go 言語の非常に重要な部分であり、モジュール化、コードの再利用、抽象化の機能を提供します。関数を使用すると、複雑なロジックを複数の小さなモジュールに分割できるため、コードがより明確になり、読みやすくなり、保守と拡張が容易になります。柔軟性と機能の多様性により、Go 言語を使用してさまざまな問題やシナリオを解決できます。

現段階ではまだ Go 言語を勉強中の段階なので、総合的に考えていないところもあるかと思いますが、この記事のサンプルはすべて手書きで書いて実行しています。
ご質問がございましたら、アドバイスをお願いいたします。
コメントで知らせてください!一緒に学び、一緒に進歩しましょう!

おすすめ

転載: blog.csdn.net/qq_19283249/article/details/132116497