私は再帰の概念を研究してきたばかりで、簡単な例を試してみようと思いました。次のコードでは、1、2、3、4、5の数値を取得し、再帰を使用してそれらを追加しようとしています。結果は15になると予想していましたが、コードは16を返します。
何が悪いのですか?
static void Main(string[] args)
{
Console.WriteLine(Sum(5));
Console.Read();
}
static int Sum(int value)
{
if (value > 0)
{
return value + Sum(value - 1);
}
else
{
return 1;
}
}
Else句で1を返しています。あなたは0を返すはずです:
else
{
return 0;
}
値がゼロ以下の場合、なぜ最初に1を返すのですか?
コードは次のように実行されます。
Sum --> 5
Sum --> 4
Sum --> 3
Sum --> 2
Sum --> 1
Sum --> 0
1 <---
2 <---
4 <---
7 <---
11 <---
16 <---
ベースケースを確認してください。
他の人はすでにエラーを指摘していたので、再帰について詳しく説明します。
現在、C#は末尾呼び出しの最適化を実行していません(ただし、ILには特別なtail
命令があります)が、末尾再帰は一般的に良いことです。
Tail recursion は、関数の最後の操作であるtail呼び出しが再帰呼び出しである再帰の特殊なケースです。最後の呼び出しは再帰呼び出しであるため、呼び出し元の関数のスタックフレームを保持する必要はありません。コンパイラはこの情報を使用して、スタックがまったく成長しないように機械命令を簡単に生成できます。したがって、基本的に再帰関数を反復関数に変えることができます。
末尾再帰をサポートするようにコードを書き換えると、次のようになります。
static int Sum(int result, int value)
{
if(value == 0)
return result;
return Sum(result + 1, value - 1);
}
static int Sum(int value)
{
if (value > 0)
{
return value + Sum(value - 1);
}
else
{
return 0; //Change this.
}
}
これは、値が= 0の場合、1を返すためです。その後、追加されます。
Sumの「else」句は0を返します。
私は常に終了ケースを前に置くことを好み、それが明白になるようにします。また、私は"if cond then return a else return b"
コンストラクト。私の選択は(負の数では正しく機能しないことを明確にする)でしょう:
static unsigned int Sum(unsigned int value) {
if (value == 0)
return 0;
return value + Sum(value - 1);
}
私はそれが中括弧と制御フローの泥沼よりはるかに読みやすいと思います。
他の人はすでにその質問に答えていますが、再帰を使用する場合、それが機能することを確認するために私がしたいことの1つは、基本ケースのチェックと1つの追加のケースを使用することです。私の場合、1でテストすると2になります。これは明らかに間違っているため、再帰を使用しない0を確認することをお勧めします。したがって、エラーが基本クラスにあることは明らかです。
一般に再帰は、チェックする必要があるものをリストすることができるので、推論するのが簡単ですが、直感が間違っているため、最初は信仰の飛躍が必要です。エッジのケースをテストするだけで数学を信頼してください失敗しますnever失敗します。
int summation(int num){
if (num==1)
return 1;
return summation(num-1)+num;
}
問題は、再帰をvalue == 1
、およびvalue == 0
。
終了式が問題です。値== 0(またはそれ以下)の場合、1ではなく0を返す必要があります(効率を上げるために、ここで認めておきましょう。明らかに問題はありません。さもなければ、このタスクに再帰は使用されません) 、値== 1で再帰を終了し、リテラル1を返して、不要な再帰レベルを1つ保存する必要があります。
最後に、再帰的なSumメソッドは次のようになります。
// version 3
public static int Sum(int startRange, int endRange)
{
if (endRange > startRange)
{
return endRange + Sum(startRange, endRange - 1);
}
if (endRange < startRange)
{
return startRange + Sum(endRange, startRange - 1);
}
return endRange;
}
StartRangeを0にハードコーディングすると、次のようになります。
// version 2
public static int Sum(int range)
{
if (range > 0)
{
return range + Sum(0, range - 1);
}
if (range < 0)
{
return Sum(range, -1);
}
return range;
}
...そしてメソッドを正の数だけに制限したい場合は、記号は必要ありません:
// version 1
public static unsigned int Sum(unsigned int range)
{
if (range > 0)
{
return range + Sum(0, range - 1);
}
return range;
}
これが、再帰を介して数値範囲を合計することへの洞察をより多く提供するのに役立つことを願っています。
次のように書くこともできます。
public static int sum(int n){
int total;
if(n==1){
total =1;
}else{
total = sum(n-1)+n;
}
return total;
}
実際、他のケースをチェックする必要はないと思います。
public static int sum(int number){
if(number > 0){
return number + sum(--number);
}
return number; // return 0 so that's why you don't need check else condition
}
using System;
using NUnit.Framework;
namespace Recursion
{
[TestFixture()]
public class Test
{
[Test()]
public void TestSum ()
{
Assert.AreEqual (Sum (new int[] { }), 0);
Assert.AreEqual (Sum (new int[] { 0 }), 0);
Assert.AreEqual (Sum (new int[] { 1 }), 1);
Assert.AreEqual (Sum (new int[] { 1, 2, 3, 4, 5 }), 15);
}
public int Sum(int[] head)
{
if (head.Length == 0) return 0;
int[] tail = new int[head.Length - 1];
for (int i = 1; i < head.Length; i++)
{
tail [i-1] = head [i];
}
return head[0] + Sum (tail);
}
}
}