Different results in Java and C++ using += in recursion

为君一笑 提交于 2019-11-30 20:45:38

I'm going to run through this for fun(3) for the sake of giving a complete answer. For those of you who are not interested why this works for C++ but not for Java, please ignore my answer.

Here is what Java is doing:

inside fun(3)

sum += sum + fn(n-1) // sum is 0

becomes

sum = 0 + fun(2) // sum is 0

Then inside fun(2)

sum = 0 + fun(1) // sum is 0

Then inside fun(1)

return 1 // sum is 0

Back inside fun(2)

sum = 0 + 1; // sum is 0

becomes

sum = 1; // sum will soon become 1

Back inside fun(3)

sum = 0 + 1; // sum is 1

becomes

sum = 1; // sum gets reset to 1

Here is what C++ is doing:

inside fun(3)

sum += fn(n-1) // sum is 0

becomes

sum = sum + fn(2) // sum is 0

Then inside fun(2)

sum = sum + fn(1) // sum is 0

Then inside fun(1)

return 1 // sum is 0

Back inside fun(2)

sum = sum + 1 // sum is 0

Becomes

sum = 0 + 1 => sum = 1 // sum will soon become 1

Back inside fun(3)

sum = sum + 1 // sum is 1

Becomes

sum = 1 + 1 // sum will soon become 2

What you should do: I do not know why C++ evaluates sum after making the function call rather than before. I do not know if this is in the specifications. But I do know that you should not be depending on this in any language. A correct solution would be:

int fun(int n) {
    if (n == 1)
        return 1;
    else
        return n + f(n - 1);
}

The problem is this line:

 sum += fun(n - 1);

which is updating the variable sum.

Assuming that you are simply trying to sum the numbers from 1 to N, then it should be doing the calculation that calculates f(N) in terms of f(N - 1). That doesn't require you to refer to sum ... and certainly it doesn't require you to update it.

(I'm being careful NOT to tell you what the answer is ... because it you will learn more if you figure it out yourself.)


By the way, there is nothing Java specific about the flaw in your algorithm ...


It is worth noting that the real issue is not to do with static versus instance variables. The real issue is that a recursive function like this shouldn't be using either kind of variable. Now in this example you can possibly get away with it, but if the recursion involves something like this: f(N) = f(N-1) + f(N-2) you are liable to find that the different call trees interfere with each other.

A more correct solution in this case is to write the method as:

int fun(int n) {
    if (n == 1)
        return 1;
    else
        return n + f(n - 1);
}

As I said, you don't need to refer to, or update the sum variable.

The current answers to this question do not get to the root of the issue. The behavior in Java is due to the fact that:

sum += fun(n - 1); 

is equivalent to:

sum = sum + fun(n - 1); 

where sum is evaluated before fun(n - 1) and the value is stored. We can see this by going to the JLS section 15.26.2. Compound Assignment Operators which says:

A compound assignment expression of the form E1 op= E2 is equivalent to E1 = (T) ((E1) op (E2))

and then the left hand operand is evaluated and then the right hand operand is evaluated and the operation is performed. So sum is evaluated before each iteration of the recursion and is 0 all the way back.

It also means that if you switched the line to this:

sum = fun(n - 1) + sum ;

it would produce the effect you desire since fun would be evaluated first.

In C++:

sum += fun(n - 1);

is also equivalent to:

sum = sum + fun(n - 1);  

this is covered in the draft C++ standard section 5.17 Assignment and compound assignment operators which says:

The behavior of an expression of the form E1 op = E2 is equivalent to E1 = E1 op E2 except that E1 is evaluated only once.[...]

The major different is that the order of evaluation of the left and right hand side is unspecified which is covered in section 1.9 Program execution paragraph 15 which says:

Except where noted, evaluations of operands of individual operators and of subexpressions of individualexpressions are unsequenced.[...]

and so 1 and 8 would both be valid results in C++. We can see this live gcc gives us 8 and clang gives us 1.

Try this:

public class Test {

   public static int fun(int n) {
      System.out.println("processing n " + n );

      if (n == 1)
        return 1;
       else{
           return n + fun(n - 1);
      }
   }

   public static void main(String[] arg) {
      System.out.print(fun(5));
   }
}
public static int fun(int n) {
    if (n == 1)
        return 1;
    else
        return n +  fun(n - 1);
} 

BTW if you want to do it in the same way as in C code, just define sum as "Integer" instead of "int"

return n <= 1 ? n : n + fun(n-1);

Try this

   static int getsum(int num){
        int sum = 0;
        if(num == 1){
            return num;

        }else{
             sum  = num+getsum(num-1);  
        }

        return sum ;
    }

You really need to step through your logic there. It doesn't make any sense. Is the "weird" output that f(5) = 8? (you've actually accidentally written a function which computes 2^(n-2), but that seems beside the point)

I can't explain to you where any syntax is wrong - its the algorithm itself. It just doesn't do what you intended it to do. A big red flag that should stand out to you: the variable n isn't even directly added to sum anywhere! When you call fun(5), that 5 isn't even used. It's just passed into f(4).

It seems to me that your logic was this: recursively loop from n->1, and add that to sum. In which case, your function should have been:

void fun(int n){
    if(n == 0)
        return;
    sum += n;
    fun(n-1);
}

or something like that. But that isn't a very...recursive-y way to do things. You'd be much better off with no variables at all. Non-base-case: return n+fun(n-1).

Also in the future, when you say that some code has "weird output", you should probably provide both 1) the weird output and 2) what you expected the output to be. We're all just guessing at what you wanted to write.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!