Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Undefined" error for rewriting but not redefining an already defined function

This works (plots an "empty" figure):

function testme
plot(1)

This works (returns 1):

function testme
plot = @(x)x;
plot(1)

This does not (Error: "Undefined function or variable 'plot'."):

function testme
if 0
    plot = @(x)x;
end
plot(1)

What's going on here? Why does rewriting but not redefining an already defined function render the function undefined?

Note 1: this is not specific for builtin functions; the following code returns the same error:

function testme
if 0
    myfun = @(x)x;
end
myfun(1)

function x=myfun(x)
x=x*2;

Note 2: the error occurs in a function environment, not in a script; the following code does not return an error (and plots the same empty figure as in the first example):

if 0
    plot = @(x)x;
end
plot(1)

Update: For the interested reader, here is some background information on my original problem. The above examples are just minimum working samples to illustrate the main issue (which indeed feature dead end if statements). In practice, I was trying to make a function useable for colleagues who did not have certain library/toolbox functions available, by overwriting those functions for simplified custom ones if they did not exist, as a quick fix. In particular, it concerned imdilate (and imerode). The function looked something like the following:

function [myoutputs] = myfunction(myinputs)

% if the images toolbox is not available, use the following simplified
% replacement function
if ~exist('imdilate','file')
    imdilate = @(IM,SE)vecdilate(IM,SE);
end

%% The original function goes here, which uses a lot of imdilate(IM,SE).

%% local functions
function M = vecdilate(IM,SE)
% simplified version of imdilate (can only process 1-D vertical arrays)

nSE = size(SE);
nIM = size(IM);
SE = logical(SE); % make logical if it isn't yet

% copy and shift xth column x down. new border entries are 0:
M = repmat([IM;zeros(nSE)],nSE);
M = M(1:end-nSE(1));
M = reshape(M,[size(M,1)/nSE(1) nSE(1)]);

% shrink back to column by taking max of every row:
M = max(M(:,SE),[],2);
M = M(ceil(nSE(1)/2)-1+(1:nIM(1))); % clip to obtain correct size

You might see that the replacement function covers some functionality of imdilate, but not all, and it might not be as efficient. The purpose simply was to use function A if it was available, and function B if it was not. To my surprise however, the former case returned an error, which eventually resulted in this question. For your interest, I solved the practical problem by renaming the function in the original code, and by using an if/else statement:

function [myoutputs] = myfunction(myinputs)

% if the images toolbox is not available, use the following simplified
% replacement function
if ~exist('imdilate','file')
    mydilate = @(IM,SE)vecdilate(IM,SE);
else
    mydilate = @(IM,SE)imdilate(IM,SE);
end

%% The original function goes here, which uses a lot of mydilate(IM,SE).

%% local functions
function M = vecdilate(IM,SE)
etc. etc. etc.
like image 443
JJM Driessen Avatar asked Dec 05 '19 14:12

JJM Driessen


1 Answers

Just-in-time-compilation (JIT) does not mean that there is no compilation and that every line is interpreted separately, so you can still mess with the code;)

The error would also appear if you use a not-defined function, where you woun't even expect the code to run, like

function [] = test()
if false
    a = @(x)x;
end
a(1)
end

Scripts are stored command line entries, i.e. the compiler has no choice but to handle every line separately (you may want to think of it as a keyboard macro). Functions in contrast are encapsulated pieces of code. The compiler (in general) does not expect anything unknown + it thinks that this encapsulated piece of code might be reused. Therefore, it makes sure to do a proper job compile all code once beforehand (if the compile would do this all the time, it is called ahead-of-time compilation).

This becomes in particular obvious when your clear the variables in between:

function [] = test()
if false
    plot = @(x)x;
else
    clear all % clear vs clear all
end
plot(1)
end

(Note that clear clears all variables but clear all would also clear excising code (see MATLAB Execution Engine))

Have a look at this interesting blog post from Loren

MATLAB provides the best of both worlds by compiling MATLAB code on-the-fly, or just-in-time. MATLAB code is compiled whether it be in classes, functions, scripts, or simply at the command line. There is no explicit compile step for the user to initiate and MATLAB code can be executed in blocks as small as one line at a time. The MATLAB JIT compiler generates native machine level code that is optimized for the MATLAB code being executed and for the specific hardware platform.

Anyway, you should not write dead ends in your code or overwrite (native) functions. It is good to use function handles to overcome this problem, but make sure that you define it for all cases

function [] = test()
if false % dead end definition
    fnc = @(x)x;
else
    fnc = @plot;
end
fnc(1)
end
like image 200
max Avatar answered Oct 20 '22 13:10

max