Logo

dev-resources.site

for different kinds of informations.

Never call the same function twice (with memoization)

Published at
7/30/2024
Categories
javascript
clojure
memoization
learning
Author
baypanic
Author
8 person written this
baypanic
open
Never call the same function twice (with memoization)

So I've just discovered this interesting little concept of memoization.

I've started reading the article about it and stopped as soon as I caught the tail of the idea.

Then I decided to figure out the simple solution in my own and in the way I understand it.

If you've never hear of it, memoization is the process of storing the results of function execution, so you can pull it from a little (or not so) cache next time you run that function with the same arguments.

In reality this can be useful for a high resources consuming function. It comes with cost of using extra space as a cache. But it can improve the speed of your code and the experience of the users who use it.

I've played with JS code a bit and came with this solution:

const memoize = fn => {
  const cache = {}
  return (...args) => {
    const fnKey = `${fn.name}(${args})`;
    if(!cache[fnKey]) {
      cache[fnKey] = fn(...args);
    }

    return cache[fnKey]
  };
}
Enter fullscreen mode Exit fullscreen mode

Then you can run it like that:

function _add(x, y) {
  console.log("function runs", x, y);
  return x + y;
}

const add = memoize(_add)

add(42, 69)
add(10, 15)
add(10, 15)
Enter fullscreen mode Exit fullscreen mode

That leads to the execution of function twice (#1 and #2 'add' calls). Third 'add' call will use cache as it's the same as #2 call.

'function runs' 42 69
'function runs' 10 15
Enter fullscreen mode Exit fullscreen mode

You can see that 'function runs' 10 15 is only called once. This is because the second time we call it, the cache is being used.

Now let's quickly break down what's going on here.

In this example we utilize closure mechanism to store the cache.

const memoize = fn => {
  const cache = {}
  return () => {

  };
}
Enter fullscreen mode Exit fullscreen mode

This allows us to throw the "fn" argument, which is the king of the party as this is exactly the function we want to operate with, down the scopes and "listen" to each of its execution.

I really have written it in the most simplistic, naive way. So we are going to use function name with arguments as keys of the cache, and the results of its execution as values.

That means, that executing:

add(2, 2)
Enter fullscreen mode Exit fullscreen mode

Results in

// Our cache
{
  'add(2, 2)': 4
}
Enter fullscreen mode Exit fullscreen mode

Cache value.

I know that this is possibly not exactly how it should be done "the right way". But the idea of this exercise and the article isn't about the well tested safe and edge cases free solution.

It's about the learning and simple implementation. About the concept. So I'm not focusing on details of implementation right now.

Now, we first figure out the key for the function call:

const memoize = fn => {
  const cache = {}
  return (...args) => {
    const fnKey = `${fn.name}(${args})`;
  };
}
Enter fullscreen mode Exit fullscreen mode

We will use it to store results of the function execution in the cache.

Then we check if this key (fnKey) already exists. If not, we set the key with its value as a result of the passed function execution.

In the end we always return the result from the cache. So really the execution of the function passed to memoize method always ends in the closure (in the "cache" object).

We only operate with this object now:

const memoize = fn => {
  const cache = {}
  return (...args) => {
    const fnKey = `${fn.name}(${args})`;
    if(!cache[fnKey]) {
      cache[fnKey] = fn(...args);
    }

    return cache[fnKey]
  };
}
Enter fullscreen mode Exit fullscreen mode

And that's it.

Now I'm gonna go and look how it should be done "properly". But if you find this interesting, let me know. If something is unclear or wrong with this approach (for your taste), just drop the comment and let's talk about it.

Thanks, see you!

clojure Article's
30 articles in total
Favicon
25 Must-Check Clojure Resources for Developers: Tutorials, Tools, and Tips
Favicon
Is it easy to manage a team of highly qualified engineers?
Favicon
[PT-BR] Functional vs OOP: Uma análise profunda dos paradigmas de programação
Favicon
From Chaos to Control
Favicon
The capacity to learn new languages is very important
Favicon
Transducer: A powerful function composition pattern
Favicon
Clojure Is Awesome!!! [PART 4]
Favicon
Episode 3: Once you try Clojure, there is no way back
Favicon
Clojure Is Awesome!!! [PART 3]
Favicon
Clojure Is Awesome!!! [PART 2]
Favicon
Clojure in Product podcast
Favicon
Early termination of transducers and reducing functions
Favicon
Clojure is Awesome!!!
Favicon
To transduce or not to transduce?
Favicon
Clojure in Product podcast, 2nd episode
Favicon
Clojure REPL-Driven Development with VS Code
Favicon
10 Soft Skills que Aprendi Durante 3 Anos Criando Soluções para 100 Milhões de Usuários
Favicon
Scheming About Clojure
Favicon
Calling Clojure from Java using a real example (Clojure + Quarkus)
Favicon
Querido Yo del Futuro: Hoy intentaremos configurar una aplicación fullstack en Clojure
Favicon
Never call the same function twice (with memoization)
Favicon
The Clojure Paradox
Favicon
Why I chose Clojure/Script for building Vade Studio
Favicon
How I’m learning Clojure in 2024
Favicon
Krestianstvo Electric Lazy Reflector for Croquet VM
Favicon
Implementing a 2d-tree in Clojure
Favicon
Need microservice? Take Clojure!
Favicon
Transducers and Eduction in Clojure simply explained
Favicon
Meet Datomic: the immutable and functional database.
Favicon
Java Allergies and Revisiting java.time

Featured ones: