# Use nested functions to memoize costly functions 27

Posted by **Loren Shure**,

function f = memoize1(F) %one-arg F, inputs testable with == x = []; y = []; f = @inner; function out = inner(in) ind = find(in == x); if isempty(ind) out = F(in); x(end+1) = in; y(end+1) = out; else out = y(ind); end end endLet's see what happens when we use this function, admittedly with not such an expensive function to calculate. Also, you could substitute any other function handle here, including one referring to an anonymous function, provided it has one input and one output.

>> f = memoize1(@sin) f = @memoize1/inner >> x = f(3) x = 0.1411 >> y = f(7) y = 0.6570 >> z = f(3) z = 0.1411 >> f(.2) ans = 0.1987 >> f(pi/8) ans = 0.3827Now let's look into what's happening with our function

`f`using the

`functions`function. This should be used primarily for debugging and gaining insight, not as a programmatic tool.

Get information about the function handle:

>> s = functions(f) s = function: 'memoize1/inner' type: 'nested' file: 'H:DocumentsLORENv7memoize1.m' workspace: {[1x1 struct]}

Get info about the nested function including its workspace:

>> s.workspace{1} ans = f: @memoize1/inner F: @sin x: [3 7 0.2000 0.3927] y: [0.1411 0.6570 0.1987 0.3827]So, if we were to put a breakpoint at

`inner`and issued the command

`f(3)`from the prompt, we'd be able to follow the logic about indexing into the saved values rather than doing the calculation again. In addition, if we wanted to quit MATLAB and come back at a later time, we could save our function handle in a MAT-file and reload it later. We would

*not*need to save anything in addition to the handle, since the associated workspace would be also be saved.

`memoize1`can clearly be improved by vectorizing it (probably using

`ismember`). Here is the latest version of this variant of the code:

function f = memoize2(F) %one-arg F, inputs testable with == %allow nonscalar input. x = []; y = []; f = @inner; function out = inner(in) out = zeros(size(in)); % preallocate output [tf,loc] = ismember(in,x); % find which in's already computed in x ft = ~tf; % ones to be computed out(ft) = F(in(ft)); % get output values for ones not already in % place new values in storage x = [x in(ft(:).')]; y = [y reshape(out(ft),1,[])]; out(tf) = y(loc(tf)); % fill in the rest of the output values end end

Variants of this memoize code pattern might be useful for functions that have recurrence relationships, recursive functions, or refining underlying meshes. It's possible that you would want to have a buffer limit so the size of the stored data in the nested function didn't grow beyond some bound. But you get the idea.

**Category:**- Efficiency,
- Function Handles,
- Memory,
- New Feature

### Note

Comments are closed.

## 27 CommentsOldest to Newest

**1**of 27

**2**of 27

**3**of 27

**4**of 27

`persistent`, you only have one instance because all invocations of a function with a persistent variable share the value (assuming regular and not nested functions). With nested functions, each instance of the function handle carries along its own workspace. I recommend you read the user documentation carefully and examine the demos and examples to understand more (especially

`nesteddemo`).

**5**of 27

**6**of 27

**7**of 27

**8**of 27

**9**of 27

**10**of 27

**11**of 27

**12**of 27

**13**of 27

**14**of 27

**15**of 27

**16**of 27

**17**of 27

a = rand(1000); tic, b = a'*a; toc tic, f = @() a'*a, toc tic, c = f(); toc

Elapsed time is 0.000887 seconds. Elapsed time is 0.081510 seconds. >> tic, c = f(); toc Elapsed time is 0.074612 seconds.Setting up the function handle takes little time, but the evaluation is similar to the time for the expression itself. --Loren

**18**of 27

pv = @(v, varargin) (v<=1) && fprintf(varargin{:}); slow = @() fprintf('slow called ...\n'); pv(1, ' when needed\n', slow()); pv(2, ' even when not needed\n', slow());would like to avoid the call to slow() in the course of the 2nd call to pv(), pv(2, ...). Thanks, Ljubomir

**19**of 27

**20**of 27

**21**of 27

pv = @(v, varargin) (v<=1) && fprintf(varargin{1},varargin{2}()); slow = @() fprintf('slow called ...\n'); pv(1, ' when needed\n', slow); pv(2, ' even when not needed\n', slow);

>> pv(1, ' when needed\n', slow); slow called ... when needed >> pv(2, ' even when not needed\n', slow); >>--Loren

**22**of 27

**23**of 27

is_verbose_level(...) && fprintf(...);Bit ugly but does it. Thanks, Ljubomir

**24**of 27

**25**of 27

**26**of 27

**27**of 27

## Recent Comments