17

Goで数値の階乗を見つけるプログラムは次のとおりです。

func factorial(x uint) uint {
    if x == 0 {
        return 1
    }

    return x * (factorial(x - 1))
}

入力 5 で呼び出されたときのこの関数の出力は 120 です。ただし、elseステートメントを追加するとエラーが発生します。

func factorial(x uint) uint {
    if x == 0 {
        return 1
    } else {
        return x * (factorial(x - 1))
    }
}

エラー :function ends without a return statement

最後に a を追加しreturnました:

func factorial(x uint) uint {
    if x == 0 {
        return 1
    } else {
        return x * (factorial(x - 1))
    }
    fmt.Println("this never executes")
    return 1
}

期待される出力 120 が返されます。

2 番目のケースでエラーが発生するのはなぜですか? 3 番目のケースでは、関数が最後の に到達しないにもかかわらず、return 1正しい出力を計算するのはなぜですか?

4

1 に答える 1

23

これはコンパイラのよく知られた問題です。

記録された問題もあります: http://code.google.com/p/go/issues/detail?id=65

Go言語の作者の一人の言葉で:

コンパイラーは、戻り値またはパニックのいずれかが、結果を伴う関数内で語彙的に最後になることを必要とします。このルールは、関数がリターンせずに最後に到達したかどうかを判断するために完全なフロー制御分析を要求するよりも簡単で (これは一般的に非常に困難です)、このような簡単なケースを列挙するルールよりも簡単です。また、純粋に語彙的であるため、関数内の制御構造で使用される定数などの値の変更によってエラーが自然発生することはありません。

-奪う

golang-nutsの別のコメントから、すぐに「修正」されることはないと推測できます。

これはバグではなく、意図的な設計上の決定です。

-奪う

Java などの他の言語には、これを許可する規則があることに注意してくださいelse


2013 年 3 月 EDIT - Go1.1で変更されました:

Go 1.1 より前では、値を返す関数は、関数の最後に明示的な "return" または panic の呼び出しを必要としていました。これは、プログラマーに関数の意味を明示させる簡単な方法でした。ただし、無限の「for」ループしかない関数など、最終的な「return」が明らかに不要な場合も多くあります。

Go 1.1 では、最終的な "return" ステートメントに関する規則がより寛大になりました。これは、関数が最後に実行されることが保証されているステートメントである、終了ステートメントの概念を導入します。例には、条件のない「for」ループや、各半分が「return」で終わる「if-else」ステートメントが含まれます。関数の最後のステートメントを構文的に終了ステートメントとして示すことができる場合、最後の "return" ステートメントは必要ありません。

この規則は純粋に構文上のものであることに注意してください。コード内の値には注意を払わないため、複雑な分析は必要ありません。

更新: 変更には後方互換性がありますが、余分な「return」ステートメントと panic の呼び出しを含む既存のコードは、手動で単純化される可能性があります。このようなコードは、go vet によって識別できます。

そして、私が言及した問題は現在、ステータス「修正済み」でクローズされています。

于 2012-11-22T15:58:56.050 に答える