Loren on the Art of MATLAB

Turn ideas into MATLAB

Delaying Evaluation of Function Inputs 8

Posted by Loren Shure,

When you call a function in MATLAB, MATLAB first evaluates all the inputs, and then passes these (possibly) computed values as the inputs. Recently Ljubomir Josifovski asked if there was a way to delay evaluation of function inputs and only evaluate them on demand. Here's what I came up with.

Contents

Use Anonymous Functions

Instead of calling the function with expressions as input values, create those expressions as anonymous functions with no inputs.

Experiment

Let's create an array and an expression to evaluate.

a = rand(2000);
tic, f = @() a'*a, toc
f = 
    @()a'*a
Elapsed time is 0.003365 seconds.

You can see that setting up the anonymous function f doesn't take a lot of time.

Quick Comparison

We aren't passing arguments here, but comparing the direct computation time for a'*a with evaluating the same quantity via the anonymous function.

tic, b = a'*a; toc
tic, c = f(); toc
Elapsed time is 0.508455 seconds.
Elapsed time is 0.482722 seconds.

Setting up the function handle takes little time, but the evaluation is similar to the time for the expression itself.

This is what timeit, Steve Eddins' utility for benchmark timing, uses just this approach. timeit helps wash out first time evaluation costs and other timing artifacts that can show up in benchmarking. So let's try using that now for the anonymous function evaluation.

timeit(f)
ans =
         0.456538375738468

How To Take Advantage

Now, if you want your function to evaluate either expression 1 or expression 2 based on something criterion, you can do so by passing in the two expressions as anonymous functions and using some logic like this.

      if criterion == true
         y = expr1();
      else
         y = expr2();
      end

What's Your Strategy?

If you want to streamline your computations, do you have a strategy for delaying evaluation of expressions until necessary? How do you do this? Let me know here.


Get the MATLAB code

Published with MATLAB® 7.11

Note

Comments are closed.

8 CommentsOldest to Newest

Gautam Vallabha replied on : 1 of 8

The use of anonymous functions to delay the evaluation is a nice idea, but it has one weakness: it repeats the evaluation of the input on every mention. E.g:

  function myFunction(x, f)
      if x > 0
        a = f();  % Line 1  
        % ... intervening code...
        b = f();  % Line 2
      else
        % don't invoke f() at all
      end
  end

Ideally, we want the following behavior:
* if x ~= 0, then f() should not be evaluated at all.
* if x == 0, then f() should be evaluated only once, at Line 1, and Line 2 should reuse the results of the evaluation. However, the anonymous function results in two evaluations.

An alternative may be a simple handle class that does the evaluation when needed & caches the result, e.g.

classdef lazyEval < handle
    
    properties(Access=private)
        fcnHandle
        cachedData
    end
    methods
        function obj = lazyEval(fcn)
            obj.fcnHandle = fcn;
        end
        
        function val = getdata(obj)
            if isempty(obj.cachedData)
                obj.cachedData = obj.fcnHandle();
            end
            val = obj.cachedData;
        end
    end

end

The class can be improved by using a subsref to get the data instead of a explicit method call.

Gautam Vallabha replied on : 2 of 8

Minor correction: The middle of the post should be:

Ideally, we want the following behavior:
* if x <= 0, then f() should not be evaluated at all
* if x > 0, then f() should be evaluated only once […]

Mukhtar Ullah replied on : 3 of 8

-Loren,
Interesting topic again. The solution you provided suits best if you are welling to pass three inputs (two expressions and a condition) instead of one (the one computed). This is against the very essence of MATLAB which places it above other programming softwares. I would take one of the expressions from your recent blog
ceil(-log10(eps(snglpi)))
as an example. Here MATLAB has given you the luxury of writing everthing as one command. This will no longer be the case if we adopt the approach asked by Ljubomir Josifovski. So we should be writing functions in such a way that every input is really used and any redundancy be implemented as conditional statements inside the function rather than passing as inputs.

-Mukhtar

Loren replied on : 4 of 8

Muktar-

I am not advocating the use of delayed evaluation in all cases. In fact, I think it’s somewhat less common than computing things as you go. However, there are simply some situations in which it is one approach to solving a particular set of problems, and not even the only approach. So choose the way(s) you prefer.

–Loren

Loren replied on : 5 of 8

Gautam-

You said “The use of anonymous functions to delay the evaluation is a nice idea, but it has one weakness: it repeats the evaluation of the input on every mention.”

That’s true in your code but doesn’t have to be. Why not compute f() once in your function and store results in a variable to reuse, e.g., the value a you computed?

–Loren

Gautam Vallabha replied on : 6 of 8

Loren:
Storing the result in a local variable allows reuse only within the local function scope. If the lazily-evaluated parameter needs to be passed to other functions or shared in some way (e.g., between different callbacks), it would be cleaner to have the caching be encapsulated within the parameter.

Of course, the above use case is quite specialized :)

Loren replied on : 7 of 8

Gautam-

It does depend what problem one is trying to solve. I was trying to solve the original one posed by Ljubomir. Storing the values in between was not a requirement of his problem as I recall.

–Loren

Mike Xue replied on : 8 of 8

We should be aware that the result of the delayed computation may not be what we want. This is true when
(1) The expression involved depends on environment. For example, when the expression need to fetch a value stored in a global variable.
(2) The expression, when computed, causes an error. In a delayed computation we may never see the error.