1

I've just asked this question (multiple errors while momoizing function inside another function) and I've got a nice answer... but! Just to understand a little more about JavaScript, I'd like to know if the momoized function can be written in this style:

function main () {
    function memoized_f(){
        //memoizing code
    }
}

EDIT: Please notice I'm not asking what is the difference in the code above, I'm asking if it is possible to memoize the second one!

So, how to rewrite this?

function main() {
  var create_node = (function() {
    var memo;
    console.log("memo: " + memo);
    console.log("create_node")

    function f() {
      var value;
      if (memo) {
        value = memo.cloneNode();
        console.log("clone node");
        console.log(value);
      } else {
        var value = document.createElement("div");
        value.innerHTML = "hello";
        console.log("new node");
        console.log("value: " + value);
        memo = value;
      }
      return value;
    }
    return f;
  })();
  var collection = [];
  for (var i = 0; i < 10; i++) {
    collection.push(create_node());
  };
  // Display results
  for (var i = 0; i < 10; i++) {
    console.log(i + ". " + collection[i]);
  }
}
main();
Community
  • 1
  • 1
Vandervals
  • 4,724
  • 5
  • 33
  • 81
  • Either one is fine, though they're not exactly the same. – Pointy Jul 27 '15 at 13:13
  • @Pointy but how to memoize the second one? – Vandervals Jul 27 '15 at 13:14
  • 2
    ?? What does that mean? There's really no meaningful difference in the two samples. In both cases, there'll be a local function called `memoized_f`. – Pointy Jul 27 '15 at 13:15
  • Yes, but see the other question's answer, you can't asign an autocallable function without using the var syntax – Vandervals Jul 27 '15 at 13:17
  • 1
    So you're really asking for the difference between `function x(){}` and `var x = (function (){})()`? – deceze Jul 27 '15 at 13:20
  • no, I'm asking how to memoize function x(){} when it is inside another function – Vandervals Jul 27 '15 at 13:21
  • "can't assign an autocallable function without using the `var` syntax" - I don't know what that means, but I'm pretty sure it's wrong. – Pointy Jul 27 '15 at 13:24
  • The main problem I'm having is that I have no idea what you mean by "memoize". – Pointy Jul 27 '15 at 13:24
  • It is quite a known term in programation: http://www.sitepoint.com/implementing-memoization-in-javascript/ – Vandervals Jul 27 '15 at 13:25
  • 1
    You seem to think that the first example can be memoized and the second case maybe can't be memorized. I (and others) don't understand why you would think the second example can't be memoized. Perhaps it would be helpful if you showed how you would memoize the first example, and use that to explain your concerns about the second case. – apsillers Jul 27 '15 at 13:26
  • I did that referencing my other question, but let me add that code, here... – Vandervals Jul 27 '15 at 13:28
  • 1
    @Vandervals You full code does not reflect your simple examples. Your first simple example is of the form `var memoized_f = function(){ }` but your actual code is of the form `var memoized_f = (function(){ })()` (where the immediately-invoked function returns a function). You may wish to clarify your examples to better reflect your actual question . – apsillers Jul 27 '15 at 13:30
  • ok, forget about the first example, you are right that it is not exactly the same case, do you think it is clearer now? – Vandervals Jul 27 '15 at 13:38

2 Answers2

1

Your actual memoized function is f. The (function(){ ... })() IIFE wrapping merely provides a an additional closure-layer to hide the variable memo so that it is visible only to f.

To repeat that: the (function(){...})() expression is not your memoized function. It is wrapping that restricts visibility of an inner variable and ultimately returns your memoized function f, which is defined inside of it. If you were okay with exposing memo to other code in main and not restrict its visibility to the memoized function only, you could eliminate the IIFE wrapping entirely and simply rename f to create_node:

function main() {
    var memo;

    function create_node() {
      var value;
      if (memo) { value = memo.cloneNode(); }
      else {
        var value = document.createElement("div");
        value.innerHTML = "hello";
        memo = value;
      }
      return value;
    }

  // use `create_node` as originally done
  // NOTE that other code can manipulate `memo` now, though!
}
main();

If you like, you can supply the closure wrapping via a function declaration instead of IIFE:

function createMemoizedFunc() {
    var memo;

    function f() {
      var value;
      if (memo) { value = memo.cloneNode(); }
      else {
        var value = document.createElement("div");
        value.innerHTML = "hello";
        memo = value;
      }
      return value;
    }
    return f;
}
var create_node = createMemoizedFunc();
apsillers
  • 101,930
  • 15
  • 206
  • 224
  • If you execute this createNode doesn't have a node, it has the function f. If I return f( ), then it always executes the else declaration. – Vandervals Jul 27 '15 at 13:55
  • nope, in my example, create_node returns a div, otherwise the function doesn't make sense, it is supposed to create divs or clone them – Vandervals Jul 27 '15 at 13:59
  • @Vandervals Yes, this `createNode` (i.e., the function returned by `createMemoizedFunc`) also returns a div; I don't understand how mine is different. I've checked, and it runs the `else` block only the first time, just as your original does. – apsillers Jul 27 '15 at 14:02
  • oh, I understand you now! createNode is the function I must use to create nodes, not an example of how to create one! Yes yes! But the thing is that I'm asking how to avoid creating variables containing functions... (if it is even possible to do it so) – Vandervals Jul 27 '15 at 14:06
  • @Vandervals How do you plan you call it if you don't assign it to a variable? You could eliminate the identifier `f` and return an anonymous function: instead of `function f() { ... }; return f;`, you simply do `return function() {...};`. I'm not sure I quite understand what you mean by "avoid creating variables". – apsillers Jul 27 '15 at 14:09
  • @Vandervals I expanded my second paragraph with a code sample. – apsillers Jul 27 '15 at 14:16
  • as far as I know, there are performance matter that make `function f( ){ }` be more performant than `var f = function( )` or `var f = (function( ){ })( )` (correct me if I'm wrong). That is why I was wondering if it was possible to use the first approach, but as you well said, the other answer makes the variable visible outside... so I don't know what is best any more... – Vandervals Jul 27 '15 at 14:18
  • I wouldn't worry too much about it being visible outside as long as its not documented as part of the public interface, or use $$ syntax (angular private convention)... If security is your concern, it wouldn't be too hard to read the memo variable if somebody really wanted to see it. – Patrick Jul 27 '15 at 14:23
  • @Vandervals Any difference in performance (if any difference exists) is going to be imperceptible unless you are *declaring* this function millions of times. (Note: *declaring* millions of times, not *calling* millions of times -- call-time performance of a function is certainly *in no way* affected by how the function was defined lexically in the source code.) How many copies of this function do you plan to create (i.e., how many thousands of times do you plan to call `main`)? – apsillers Jul 27 '15 at 14:27
  • But also the behaviour changes, as `function f( ){ }` is read before execution, right? – Vandervals Jul 27 '15 at 14:32
  • @Vandervals The entirely of `main` is read before execution, but yes, function declarations like `function f( ){ }` are available immediately at the top of their containing scope rather than at whatever point they are lexically defined in the source code (a phenomenon sometimes called "hoisting"). – apsillers Jul 27 '15 at 14:58
1

Since functions in javascript are an object, you can just use that function to memoize the value. I think it would make more sense in fib example, but here is your original post.

function main() {
  // memoizing function
  function create_node() {
    var value;
    // read from memo on function object
    if (create_node.memo) {
      value = create_node.memo.cloneNode();
      value.innerHTML = 'cloned';
      console.log("clone node");
      console.log(value);
    } else {
      var value = document.createElement("div");
      value.innerHTML = "hello";
      console.log("new node");
      console.log("value: " + value);
      // retain memo on the function object
      create_node.memo = value;
    }
    return value;
  }

  var collection = [];
  for (var i = 0; i < 10; i++) {
    collection.push(create_node());
  };
  // Display results
  for (var i = 0; i < 10; i++) {
    console.log(i + ". " + collection[i]);
    document.getElementById('container').appendChild(collection[i]);
  }
}

main();
<div id="container"></div>
Patrick
  • 6,518
  • 3
  • 19
  • 31
  • 2
    Note that this will allow `create_node.memo` to be accessed and manipulated by any code that can access the `create_node` function. The closure-wrapping in the OP's original ensures that `memo` is visible only to the memoized function and nowhere else. (This answer is a perfectly serviceable approach, if that's what you want, though! It's unclear whether the OP cares about this requirement or not.) – apsillers Jul 27 '15 at 14:13
  • Absolutely true and great to point out. I was just answering the question of how to rewrite by using a function itself... Obviously the pattern was used with the Immediately Executed function for a reason (as to keep the memo private). – Patrick Jul 27 '15 at 14:17
  • Isn't it possible to do both? – Vandervals Jul 27 '15 at 14:20
  • It is with the closure provided either by an immediately executed function or calling a "factory" function that returns the memoized function. The problem is the function needs to be returned and stored somewhere (e.g. inside a variable) -- so you end up with something similar to the first pattern again. – Patrick Jul 27 '15 at 14:25