2017-09-05 2 views
7

私はプログラミングの良い習慣について学び、私はこの質問に固執しています。私はJavaで、再帰関数は(時には)「お尻の痛み」になる可能性があることを知っています。そして、私はその関数の末尾のバージョンを実装できるようにしようとしています。これで迷惑をかけることに価値があるのですか、私は昔ながらのやり方でやりますか?プログラミング用にJava/Kotlinを使用する場合は、テール再帰または反復バージョンを使用することをお勧めしますか?パフォーマンスに違いはありますか?

tailrec fun tail_fibonacci(n : BigInteger, fib1 : BigInteger = BigInteger.ZERO , fib2 : BigInteger = BigInteger.ONE) : 
BigInteger { 
    return when(n){ 
     BigInteger.ZERO -> fib1 
     else -> tail_fibonacci(n.minus(BigInteger.ONE),fib1.plus(fib2),fib1) 
    } 
} 

fun iterative_fibonacci(n: BigInteger) : BigInteger { 
    var count : BigInteger = BigInteger.ONE 
    var a : BigInteger = BigInteger.ZERO 
    var b : BigInteger = BigInteger.ONE 
    var c : BigInteger 
    while(count < n){ 
     count += BigInteger.ONE 
     c = a + b 
     a = b 
     b = c 
    } 
    return b 
} 

答えて

3

iterative_fibonacciは1つの引数をとり、かなり明確ですが、tail_fibonacciは3つで、デフォルトが提供されていますが、これはユーザーを驚かせるかもしれません。ただし、ラッパー関数を作成して、tailrec関数をlocal oneにすることもできます。

は2つの機能がcount += BigInteger.ONEn.minus(BigInteger.ONE)以外にコンパイルされたバイトコードに多くの違いがあってはならない、とあなたはa Kotlin bytecode viewerを自分のためにそれを確認することができます。

2つの実装の間に予測可能な違いはありません(JVMはJITコンパイラを使用して実行時にコードを最適化することにも注意してください)。もちろん、tailrec関数は通常の再帰関数だろう。

多くのテール再帰的解法はより自然であり(数学表記に近い)、いくつかの方法はありません(たとえば、多くのパラメータが完全な混乱)。

パフォーマンスツールとして(特にスタックオーバーフローを回避する方法として)、tailrecを使用することをお勧めします。再帰的な定義が見つかった場合は、すべての再帰呼び出しが必要です。コードは行います。

1

彼らは、パフォーマンスの面で同等です: (Kotlinで)この2つの関数のいずれかの違いがあります。 Kotlinはtailrec関数の再帰を最適化します。つまり、ループベースのアプローチと同じです。

機能的または反復的なアプローチを選択するかどうかは、可読性、簡潔さ、直感性を考慮して、実際に自分の好み(および該当する場合はあなたのチーム)に下がります。この判断はメソッドごとに異なる場合があります。 1つの関数は、関数アプローチを使用してより読みやすくなり、別の関数は直接的なループの恩恵を受ける可能性があります。

Kotlinの素晴らしい点は、両方のアプローチをサポートし、開発者がジョブに必要なツールを使用できることです。

関連する問題