0

Please read the entirety of the question before answering.

I was looking at the you can't do JavaScript under pressure quiz and got to the question about summing values in an array. I wrote the following function (originally without console.log statements) and it wasn't behaving as it should with the input [[1,2,3],4,5] - returning 10 instead of 15. Eventually I figured out how to fix it - add var in front of n and sum. I've been executing this while debugging while in Firefox's Scratchpad, viewing the console output in Firebug.

function arraySum(i) {

    // i will be an array, containing integers, strings and/or arrays like itself.
    // Sum all the integers you find, anywhere in the nest of arrays.

    n=0;
    sum=0;
    console.log(i.length)
    while(n<i.length){

        console.log("i["+n+"]="+i[n]);

         if(i[n].constructor==Array){
             console.log("array n="+n)
            sum+=arraySum(i[n]);
            console.log("out array n="+n)
        }
        else if(typeof(i[n])=="number"){
            console.log("number")
        sum+= i[n];

        }
        n++;
        console.log("sum="+sum+" n="+n)
    }
    return sum

}
console.log(arraySum([1,[1,2,3],2] ) );

The output is

start
Scratchpad/1 (line 9)
3
Scratchpad/1 (line 17)
i[0]=1
Scratchpad/1 (line 20)
number
Scratchpad/1 (line 28)
sum=1 n=1
Scratchpad/1 (line 33)
i[1]=1,2,3
Scratchpad/1 (line 20)
array n=1
Scratchpad/1 (line 23)
3
Scratchpad/1 (line 17)
i[0]=1
Scratchpad/1 (line 20)
number
Scratchpad/1 (line 28)
sum=1 n=1
Scratchpad/1 (line 33)
i[1]=2
Scratchpad/1 (line 20)
number
Scratchpad/1 (line 28)
sum=3 n=2
Scratchpad/1 (line 33)
i[2]=3
Scratchpad/1 (line 20)
number
Scratchpad/1 (line 28)
sum=6 n=3
Scratchpad/1 (line 33)
out array n=3
Scratchpad/1 (line 25)
sum=7 n=4
Scratchpad/1 (line 33)
7

So eventually I figured out that when the function is recursively called, the outer function's n variable is reset to 0 and modified up to 3, so when it exits, instead of looping one more time (which it would do if n were 2) it leaves the function. This all makes sense until you consider the sum variable, which should be under the same conditions: reset to 0 in the recursive call, then ends up as 6 when it exits the recursive call of the function,

So my question is this:

Why am I getting 7 instead of 6?

Steve
  • 4,415
  • 1
  • 19
  • 30
  • 4
    FYI, this has nothing to do with recursive functions (in general), but with the fact that variables are **global** if you don't use the `var` keyword. I.e. it behaves as if you had declared the variables outside of the function. See [Difference between using var and not using var in JavaScript](http://stackoverflow.com/q/1470488/218196). – Felix Kling Oct 23 '13 at 10:18
  • what is the difference between the variables sum and n, though? They're declared next to each other but are being treated differently. – Steve Oct 23 '13 at 10:34

2 Answers2

2

Try execute it in the debugger. I try to be precise without going one step at a time:

  • End 1st iteration: sum = 1, n = 1
  • "Go into" sub-array: sum += arraySum(i[n]); equivalent: sum = sum + arraySum(i[n]); At this time, sum = 1, and the value of arraySum needs to be determined.
  • The sub-array calculates correctly to 6, because n and sum is reset.
  • So the expression of bullet 2 evaluates to sum = 7
  • n is not reset from the previous call and points beyond the end of the array. Therefore the value 7 is returned.

For the code to work as intended, it is essential that you use var and you have wrongly parenthesized the following:

else if(typeof (i[n]=="number") )
So typeof applies to i[n]=="number" which is not what you want (it is always boolean!)

Besides, I would suggest a as the input array, i as iterator, and n as length (n = a.length).

AKo
  • 61
  • 5
  • I understand it is incorrect, but it is incorrect in an inconsistent manner. n and sum are being treated differently. Please read the actual question. Good spot on the brackets though, fixing that has no effect on the output. – Steve Oct 23 '13 at 10:36
  • Here you go, nothing spooky going on with scoping. – AKo Oct 23 '13 at 11:04
  • Right, so the sum= sum +arraySum(i[n]); is evaluating sum as 1, storing that value then calculating arraySum(i[n]) as 6 and adding together. Interesting that if you change that line to sum=arraySum(i[n])+sum then you get 12. – Steve Oct 23 '13 at 11:19
  • That's because `sum` is evaluated after `arraySum` and therefore `sum` equals the sum that was determined by the last `arraySum`. That leads to a doubling of the value. – AKo Oct 23 '13 at 11:35
1

ECMA-262 (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Language_Resources) defines:

11.13.2 Compound Assignment ( op= )

The production AssignmentExpression : LeftHandSideExpression AssignmentOperator AssignmentExpression, where AssignmentOperator is @= and @ represents one of the operators indicated above, is evaluated as follows:

  1. Let lref be the result of evaluating LeftHandSideExpression.
  2. Let lval be GetValue(lref).
  3. Let rref be the result of evaluating AssignmentExpression.
  4. Let rval be GetValue(rref).
  5. Let r be the result of applying operator @ to lval and rval.
  6. Throw a SyntaxError exception if the following conditions are all true:
    • Type(lref) is Reference is true
    • IsStrictReference(lref) is true
    • Type(GetBase(lref)) is Environment Record
    • GetReferencedName(lref) is either "eval" or "arguments"
  7. Call PutValue(lref, r).
  8. Return r.

Which in your example for code sum += arraySum(i[n]) means:

  1. get sum variable
  2. get value of sum and store it temporarily in lval internal variable
  3. get referece to function call arraySum(i[n])
  4. get result of function call and store it in rval internal variable
  5. add lval and rval
  6. make sure there is no problems
  7. store result in your sum valiable
  8. also return this result

Your had problems with recursion, because when you called arraySum second time, you overriden global variables n and sum. Override of sum was not problem, because you used += operator, but you ended your first call of arraySum after you rewind n to 4.

MBO
  • 30,379
  • 5
  • 50
  • 52