mjolk
mjolk

Reputation: 31

How to debug and inspect a recursive function?

I was wondering if someone could explain to me the following solution:

function smallestInt(arr) {
 if (arr.length === 1) return arr[0];
 return (Math.min(arr[0],smallestInt(arr.slice(1))));
}
console.log(smallestInt([42, 12, 8, 60, 12, 33, 21]));  // 8

In particular, what the stack would look like. Any input would be greatly appreciated.

Upvotes: 0

Views: 219

Answers (4)

customcommander
customcommander

Reputation: 18901

Using console.log is great for quick debugging. It's easy to reach for and is a great tool to have in your box but it is not the only one. There's much to be gained in learning how to use a debugger too.

Setting up the debugging session

  1. Open the Dev Tools and select the Sources tab
  2. Create a new snippet and add your code
  3. In the left margin click on L2. The blue arrow is a breakpoint.
  4. Run your code

enter image description here

Running the debugging session

The debugger will now stop every single time L2 is reached. See the screencast below. Note how the debugger displays the current state of arr in the top right corner:

enter image description here

Another thing you can do is "go back in time". Note how we can inspect each step in the stack back and forth. (See bottom right.)

enter image description here


Unless it is an exercise you don't need recursion for this. Here are a couple of alternatives:

Math.min(...[42, 12, 8, 60, 12, 33, 21]);
//=> 8

Or with reduce:

[42, 12, 8, 60, 12, 33, 21].reduce((n, x) => Math.min(n, x), Infinity)
//=> 8

Upvotes: 1

navnath
navnath

Reputation: 3714

Lets take small array [42, 12, 8, 60]

So Its start with finding min value between 42 and result returned by resursive call to smallestInt([12, 8, 60]). Here we call smallestInt by removing 1st index element from our array. If we were to expand this call stack out, it would look like this:

smallestInt([42, 12, 8, 60]) = Math.min(42, smallestInt([12, 8, 60]));

Now our previous smallestInt call will have more than 1 length array so it does make further recursive call to self by setting 1st element as a first parameter to Math.min and second parameter as calling smallestInt with array removing 1st index element. i.e. 12. So our call stack will looks like this.

smallestInt([42, 12, 8, 60]) = Math.min(42, Math.min(12, smallestInt([8, 60]));

And again same as previous:

smallestInt([42, 12, 8, 60]) = Math.min(42, Math.min(12, Math.min(8, smallestInt([60]))));

Since now our smallestInt([60]) gets 1 length array [60] as input; if (arr.length === 1) condition gets satisfied. i.e smallestInt will return 60. And we break out of recursive call.

Now It starts traversing reverse. ie. It will start finding min value.

smallestInt([42, 12, 8, 60]) = Math.min(42, Math.min(12, Math.min(8, 60)));
smallestInt([42, 12, 8, 60]) = Math.min(42, Math.min(12, 8));
smallestInt([42, 12, 8, 60]) = Math.min(42, 8);
smallestInt([42, 12, 8, 60]) = 8;

And finally returns 8;

Upvotes: 0

DecPK
DecPK

Reputation: 25408

The following steps can be log output as

let stage = 0;

function smallestInt(arr) {
  console.log(`arr = [${arr}]`);
  if (arr.length === 1) return arr[0];

  let temp, newArr;
  const min = Math.min(arr[0], temp = smallestInt(newArr = arr.slice(1)));
  console.log(`stage: ${stage++} => Math.min(arr[0] = ${arr[0]}, smallestInt( arr.slice(1)) = [${newArr}] = ${min}`)

  return min;
}
console.log(smallestInt([42, 12, 8, 16]));
/* This is not a part of answer. It is just to give the output fill height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }

Let's go process step by step

1) When the function starts

arr = [42,12,8,16]

NOTE: and we know that arr.length is not equal to 1 so it won't return arr[0]

and we are returning the result value of

Math.min(arr[0],smallestInt(arr.slice(1)))

which can be interpreted as:

Math.min(arr[0] = 42, smallestInt( arr.slice(1)) = [12,8,16] // recursion here

2) When the function starts

arr = [12,8,16]

NOTE: and we know that arr.length is not equal to 1 so it won't return arr[0]

and we are returning the result value of

Math.min(arr[0],smallestInt(arr.slice(1)))

which can be interpreted as:

Math.min(arr[0] = 12, smallestInt( arr.slice(1)) = [8,16] // recursion here

3) When the function starts

arr = [8,16]

NOTE: and we know that arr.length is not equal to 1 so it won't return arr[0]

and we are returning the result value of

Math.min(arr[0],smallestInt(arr.slice(1)))

which can be interpreted as:

Math.min(arr[0] = 8, smallestInt( arr.slice(1)) = [16] // recursion here

4) When the function starts

arr = [16]

NOTE: The arr.length is equal to 1 so it will return arr[0] that is 16


3) Now the value returned from smallestInt( newArr = arr.slice( 1 ) ) is 16 so the returned value of function will be

Math.min(8, 16)  ===> 8 will be out returned value of whole function

2) Now the value returned from smallestInt( newArr = arr.slice( 1 ) ) is 8 so the returned value of function will be

Math.min(12, 8)  ===> 8 will be out returned value of whole function

1) Now the value returned from smallestInt( newArr = arr.slice( 1 ) ) is 8 so the returned value of function will be

Math.min(42, 8)  ===> 8 will be out returned value of whole function

So the final value of console.log( smallestInt( [42, 12, 8, 16] ) ); will be 8

Upvotes: 0

vaira
vaira

Reputation: 2270

this is simply comparing the last and the second last index and selecting the smaller in each step.

// You can print it with slight rearrangement of the code 
   
function smallestInt(arr) {
    if (arr.length === 1) return arr[0];

    let last = smallestInt(arr.slice(1))
    let res = (Math.min(arr[0], last));

    console.log(arr[0], last, res);

    return res;
}
console.log(smallestInt([42, 12, 8, 60, 12, 33, 21]));

Upvotes: 0

Related Questions