2

Beginner javascript-er here...

I came across a problem where I had to use use recursion to find out whether a given number (n) is even. Here was my solution - which passes all tests - but I'm wondering on one piece where the word "return" is needed - see below comments:

var isEven = function(n) {
  if (n < 0){
    n = Math.abs(n);
  }

  if(n === 1){
    return false;
  } else if (n === 0){
    return true;
  } else {
    return isEven(n-2);  // here is my question - can you explain why does this need the 'return' in there?
  }
};
3
  • 1
    Without that you wouldn't have a return value when n > 1 so the function would just return undefined instead of true or false. Commented Jun 7, 2018 at 4:31
  • That is where you have the recursive call.. So yes, you need it.. Commented Jun 7, 2018 at 4:31
  • You didn't test your function with a decimal number ... Commented Jun 7, 2018 at 4:37

4 Answers 4

3

here is my question - can you explain why does this need the 'return' in there?

It has to do with function calls, and how the results of a function are passed back to the caller.

Forget recursion for a moment, and lets look at a pair of functions.

function foo() {
  return 'foo for you';
}

function bar() {
  foo();
}

What happens if I call bar() like so:

console.log(bar());

Expected output:

'undefined'

foo() is executed, but the results of the functional call are ignored (e.g. not saved to a variable, nor returned). bar() has no explict return statement, so by the ECMA specification, an implicit return undefined; is used.

Looking at the call stack (note that this is written like a stack datastructure with the current function call at the top of the stack):

foo() ==> returns 'foo for you'
bar() ==> returns 'undefined'

Which results in undefined being passed into the console output function.

If we modify bar() like so:

function bar() {
 return foo();
}

Our output changes to:

'foo for you'

The result of foo() is returned as the result of bar() examining the call stack

foo() ==> returns 'foo for you'
bar() ==> returns foo() which returns 'foo for you'

Going back to your recursive example, without the return statement, it will execute, but the results of that execution won't be passed up the call stack.

Let's pretend the return statement is missing, and inspect the callstack, when n = 4.

isEven(0) ==> returns true
isEven(2) ==> returns undefined
isEven(4) ==> returns undefined

isEven(4) = undefined. ERROR.

The ultimate expected value, true never gets passed up the callstack.

As written, with the return value, this is the result:

isEven(0) ==> returns true
isEven(2) ==> returns isEven(0) which returns true
isEven(4) ==> returns isEven(2) which returns isEven(0) which returns true

isEven(4) = true PASSED.

Sign up to request clarification or add additional context in comments.

Comments

1

isEven(n - 2) is called, and evaluated.

Once it finishes, the caller function takes its evaluation, and returns it back -- to its caller.

Comments

1

Through the recursive call you get "up" the callstack:

--> isEven(6) --> isEven(4) --> isEven(2) --> isEven(0)

Now you return true from the upper call:

--> isEven(6) --> isEven(4) --> isEven(2) --> isEven(0)
  ? <--                                   <-- true

But that does not reach the original caller yet. So we have to return it from isEven(2) and so on so that it reaches the caller:

--> isEven(6) --> isEven(4) --> isEven(2) --> isEven(0)
 true <-     <--           <--           <--

Comments

1

JavaScript is a multi-paradigm language, and as such, you can write valid JavaScript programs in a variety of styles.

In older JavaScript programs, functions could only be declared using function syntax which defined a function body between two curly braces, { ... }.

The function body is made up of statements. One such statement is the return statement which allows a function to return a value. Without the return statement, a function will always return undefined.

So from one perspective you can say "You need a return statement otherwise you will get undefined", but from another perspective you could say "You're using function syntax, so you must use return if you expect a return value".

The alternative is a syntax available to you in newer JavaScript programs (ES6 and up), called arrow functions. Arrow functions can accept a traditional function body with statements just like we talked about above. But arrow functions can also be written using a single expression as the body, where the return is implied

// function syntax
var f = function () { return 1 }

// arrow syntax with body
var f = () => { return 1 }

// arrow syntax with implied return   
var f = () => 1

If you can manage to write your function using an expression, you don't have to worry about all of the strange behaviors associated with statements like return.

To make logical decisions below, we use a ternary expression. Unlike the if statement, a ternary expression evaluates to a value, which when coupled with an arrow function, we can imagine the logical branches as each having their own implicit return ...

const isEven = (n) =>
  n === 0
    ? true         // implicit return
  
  : n === 1
    ? false        // implicit return
  
  : isEven (n - 2) // implicit return
  
console.log
  ( isEven (0)  // true
  , isEven (1)  // false
  , isEven (2)  // true
  , isEven (3)  // false
  , isEven (4)  // true
  , isEven (5)  // false
  )

Of course that's just one way. Here's another

const isEven = (n) =>
  n < 2
    ? !Boolean (n)
    : isEven (n - 2)

console.log
  ( isEven (0)  // true
  , isEven (1)  // false
  , isEven (2)  // true
  , isEven (3)  // false
  , isEven (4)  // true
  , isEven (5)  // false
  )

And another using mutual recursion

const isEven = (n) =>
  n === 0
    ? true
    : isOdd (n - 1)
    
const isOdd = (n) =>
  n === 0
    ? false
    : isEven (n - 1)

console.log
  ( isEven (0)  // true
  , isEven (1)  // false
  , isEven (2)  // true
  , isEven (3)  // false
  , isEven (4)  // true
  , isEven (5)  // false
  )
  
console.log
  ( isOdd (0)  // false
  , isOdd (1)  // true
  , isOdd (2)  // false
  , isOdd (3)  // true
  , isOdd (4)  // false
  , isOdd (5)  // true
  )

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.