Search code examples
javascriptoptimizationtail-recursionarticle

Question on "Tail Call Optimization" Article


I have a question regarding this article.

Between this code

function odds(n, p) {
  if(n == 0) {
    return 1
  } else {
    return (n / p) * odds(n - 1, p - 1)
  }
}

and this code

(function(){
  var odds1 = function(n, p, acc) {
    if(n == 0) {
      return acc
    } else {
      return odds1(n - 1, p - 1, (n / p) * acc)
    }
  }

  odds = function(n, p) {
    return odds1(n, p, 1)
  }  
})()

1) I'm confused about how much this helps. Does the second snippet simply have a tail call that creates less overhead because it calculates what it needs before it calls itself again, or is there something more I'm missing?

As I understand it, the tail call still isn't eliminated, just optimized.

2) And why does there need to be odds and odds1 anyway? It still isn't clear to me either.


Solution

  • I'm confused about how much this helps. Does the second snippet simply have a tail call that creates less overhead because it calculates what it needs before it calls itself again, or is there something more I'm missing?

    As I understand it, the tail call still isn't eliminated, just optimized.

    If the end of a procedure looks like this:

    push args
    call foo
    return
    

    Then the compiler can optimize that away to just

    jump startOfFoo
    

    Eliminating the procedure call entirely.

    And why does there need to be odds and odds1 anyway? It still isn't clear to me either.

    The "contract" of odds specifies only two arguments - the third argument is just an implementation detail. So you hide that away in an internal method, and present a "wrapper" as the external API.

    You could call odds1 something like oddsImpl and it would make it clearer, I think.