Splice and shift functions not working as expected in recursion

Viewed 142

I'm writing my first lines of JS code, now I'm trying to do an easy recursive function.

I know splice() and shift() removes the item they extract.

I'm having troubles here:

function sub (...par) { // It could take an arbitrary number of parameters

    if (par.length >= 2)
    {
        var val = par.shift();
        //val = par.splice(0,1);
        return val - sub(par);
    }
    else 
    {
        /*EDIT*/ //return par[0];
        return -par[0];
    }
}

Now if I run: sub (17,7) I correctly got 10, but If I run sub(17,7,3) instead of getting 8 I got NaN.

fixed like this thanks to Jonas Wilms help:

function sub (...par) { // It could take an arbitrary number of parameters

    if (par.length >= 2)
    {
        var val = par.shift();
        //val = par.splice(0,1);
        return val - sub(...par);
    }
    else 
    {
        /*EDIT*/ //return par[0];
        return -par[0];
    }
}
2 Answers

You have to spread on the recursive call:

  return val - sub(...par);

Otherwise par inside of the called sub will be an array containing an array, and doing substraction on that will fail.

However the whole thing is way more elegant if you take the first parameter directly:

  function sub(head, ...rest) {
    if(!rest.length) return head;
    return head - sub(...rest);
  }

However one should note that you might not get the result you expect with that, as you calculate the right side before the left, so

  sub(1, 2, 3)

equals

 1 - (2 - 3)

and thats 2.

I think it would be clearer if sub(1, 2, 3) would equal 1 - 2 - 3 that can be achieved with:

 function sub(...rest) {
   if(rest.length === 1) return rest[0];
   const last = rest.pop();
   return sub(...rest) - last;
 }

Or without recursion:

  const sub = (...args) => args.reduce((a, b) => a - b);

Consider this total implementation that returns a correct value even when zero arguments are used

const sub = (a = 0, b = 0, ...more) =>
  more.length === 0
    ? a - b
    : sub (a - b, ...more)

console.log(sub(17))         // 17
console.log(sub(17,7))       // 17 - 7 = 10
console.log(sub(17,7,3))     // (17 - 7) - 3 = 7
console.log(sub(17,7,3,2))   // ((17 - 7) - 3) - 2 = 5
console.log(sub(17,7,3,2,4)) // (((17 - 7) - 3) - 2) - 4 = 1
console.log(sub())           // 0

The above program is left associative whereas the program below is right-associative -

const sub = (a = 0, ...more) =>
  more.length === 0
    ? a
    : a - sub (...more)

console.log(sub(17))         // 17
console.log(sub(17,7))       // 17 - 10 = 10
console.log(sub(17,7,3))     // 17 - (7 - 3) = 13
console.log(sub(17,7,3,2))   // 17 - (7 - (3 - 2)) = 11
console.log(sub(17,7,3,2,4)) // 17 - (7 - (3 - (2 - 4))) = 15
console.log(sub())           // 0

Related