関数本体のない関数宣言に遭遇した場合、それは関数が Go に実装されていないことを意味します。
package math func Sin(x float64) float //implemented in assembly language
関数の戻り値ごとに変数名が設定されている場合、対応するゼロ値で初期化され、関数の return ステートメントでオペランドが省略されます。この使用法はベアと呼ばれます。戻る。
Go のエラー処理では、最初に一連の初期化チェックを実行し、失敗ロジックを処理するコードを最初に処理し、次に関数の実際のロジックを処理するのが一般的です。これにより、コードがより簡潔になり、エラーが回避されます。エラーが多すぎます。階層。
関数を定義するとき、関数の型をパラメータまたは戻り値の型として使用できます。これはクロージャを実現するためのデリゲートに似ていますか?ラムダ式に似た匿名関数もあります。 strings.Map 関数は実験に使用できます。
func squares() func() int { var x int return func() int { x++ return x * x } } func main() { f := squares() fmt.Println(f()) // "1" fmt.Println(f()) // "4" fmt.Println(f()) // "9" fmt.Println(f()) // "16" }
無名関数と正方形に変数参照があります。これが、関数の値が参照型であり、関数の値が比較できない理由です。 Go は関数値を実装するためにクロージャ技術を使用しており、Go プログラマーは関数値をクロージャとも呼びます。
golang Bible の匿名関数セクションにあるサンプル プログラムに注目してください。
Go 言語の可変パラメーター関数は非常に使いやすく、同じ型の複数のパラメーターを渡すことも、この型のスライスを直接渡すこともできます (..スライスを渡すときに . マークを付けます。同じスライス パラメータを区別するためだと思いますが、結局のところ、この 2 つはまだ若干異なります)。異なるタイプの変数パラメータを使用したい場合は、ユニバーサル インターフェイス{}を使用してください。そして関数本体の変数パラメータをスライスのように解析するだけです。
defer文を含む関数が実行されるまでは、defer文を含む関数がreturnで正常終了するか、パニックにより異常終了するかに関わらず、defer以降の関数は実行されません。関数内で複数の defer ステートメントを実行できます。その実行順序は宣言の順序と逆です
var mu sync.Mutex var m = make(map[string]int) func lookup(key string) int { mu.Lock() defer mu.Unlock() return m[key] }
複雑なプログラムをデバッグする場合、defer メカニズムは、関数の開始および終了のタイミングを記録するためによく使用されます。
func bigSlowOperation() { defer trace("bigSlowOperation")() // don't forget the extra parentheses // ...lots of work… time.Sleep(10 * time.Second) // simulate slow operation by sleeping } func trace(msg string) func() { start := time.Now() log.Printf("enter %s", msg) return func() { log.Printf("exit %s (%s)", msg,time.Since(start)) } }
最初に double の戻り値に名前を付けてから defer ステートメントを追加するだけで、double が呼び出されるたびにパラメーターと戻り値を出力できます。
func double(x int) (result int) { defer func() { fmt.Printf("double(%d) = %d\n", x,result) }() return x + x } _ = double(4) // Output: // "double(4) = 8"
問題の診断を容易にするために、ランタイム パッケージを使用すると、プログラマはスタック情報を出力できます。次の例では、main 関数内の printStack の呼び出しを遅延させることでスタック情報を出力します。
gopl.io/ch5/defer2 func main() { defer printStack() f(3) } func printStack() { var buf [4096]byte n := runtime.Stack(buf[:], false) os.Stdout.Write(buf[:n]) }
同じ名前のフィールド名とメソッド名を構造体に定義できないのは少し不思議です。
関数ポインタ: go には実際には関数ポインタがあります。go 言語を使用してテーブル駆動モードを実装してみましょう。
package main import ( "fmt" ) func add(a int, b int) int { return a + b } func sub(a int, b int) int { return a - b } func main() { fm := make(map[int]func(int, int) int) fm[1001] = add fm[1002] = sub protocol := 2001 i := 1 j := 2 if func_handle, ok := fm[protocol]; ok { println(func_handle(i, j)) } else { fmt.Printf("protocol: %d not register!", protocol) } }
ローカル変数ポインタを返す:
C 言語とは異なり、GO 関数はローカル変更ポインタを返すことができ、コンパイラはエスケープ解析を使用してヒープにメモリを割り当てるかどうかを決定します。
コンパイル時に -gcflags "-l -m" パラメーターを使用して関数のインライン化を無効にすることができます。関数のインライン化はメモリ割り当てにある程度の影響を与えますが、詳細は不明です。
関数パラメータにはいわゆる参照渡しはなく、すべて値によって渡されます。唯一の違いは、コピー オブジェクトが渡されるか、ポインタが渡されるかです。 C 言語では、オブジェクトのコピーを避けて効率を向上させるために、ポインター パラメーターを渡すことが一般的に推奨されます。
しかし、Go では、コピーされたポインターはターゲット オブジェクトのライフ サイクルを延長し、ヒープ上に割り当てられる可能性もあります。パフォーマンスの消費は、ヒープ メモリの割り当てとガベージのコストに追加されます。スタック上の小さなオブジェクトのコピーは実際には非常に高速であるため、オブジェクトが特に大きくない場合、または元のオブジェクトを実際に変更する必要がある場合は、通常、ポインター パラメーターを渡す必要はありません。並行プログラミングでは、データ同期の問題を排除できる不変オブジェクト (読み取り専用またはコピー) の使用も推奨されています。
次のコードでは、ヒープにメモリが割り当てられます。コンパイル時に、-gcflags "-m" を渡してアセンブリ コードを表示します:
func test(p *int) { go func() { println(p) }() } func main() { x := 100 p := &x test(p) }
出力パラメータを使用します。戻り値を使用することをお勧めします。 、次の 2 つのレベル ポインタを使用することもできます。
func test(p **int) { x := 100 *p = &x } func main() { var p *int test(&p) println(*p) }
Go 言語の知識について詳しくは、PHP 中国語 Web サイトの go 言語チュートリアル 列に注目してください。
以上がGo言語の関数とメソッドの紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。