重写但不重新定义已定义函数的“未定义”错误

JJM*_*sen 7 matlab undefined

这有效(绘制一个“空”图):

function testme
plot(1)
Run Code Online (Sandbox Code Playgroud)

这有效(返回 1):

function testme
plot = @(x)x;
plot(1)
Run Code Online (Sandbox Code Playgroud)

这不会(错误:“未定义的函数或变量'绘图'。”):

function testme
if 0
    plot = @(x)x;
end
plot(1)
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?为什么重写但不重新定义已定义的函数会使函数未定义?

注 1:这不是特定于内置函数的;以下代码返回相同的错误:

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

function x=myfun(x)
x=x*2;
Run Code Online (Sandbox Code Playgroud)

注2:错误发生在函数环境中,而不是脚本中;以下代码不会返回错误(并绘制与第一个示例中相同的空图):

if 0
    plot = @(x)x;
end
plot(1)
Run Code Online (Sandbox Code Playgroud)

更新:对于感兴趣的读者,这里是我的原始问题的一些背景信息。上面的例子只是说明主要问题的最少工作示例(它们确实具有死胡同的 if 语句)。在实践中,我试图通过将这些函数覆盖为不存在的简化自定义函数来为没有某些库/工具箱函数的同事提供可用的函数,作为快速修复。特别是,它涉及 imdilate(和 imerode)。该函数如下所示:

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
Run Code Online (Sandbox Code Playgroud)

您可能会看到替换函数涵盖了 imdilate 的某些功能,但不是全部,并且可能效率不高。目的只是在可用时使用函数 A,如果不可用则使用函数 B。然而令我惊讶的是,前一个案例返回了一个错误,最终导致了这个问题。为了您的兴趣,我通过重命名原始代码中的函数并使用 if/else 语句解决了实际问题:

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.
Run Code Online (Sandbox Code Playgroud)

max*_*max 7

即时编译 (JIT) 并不意味着没有编译并且每一行都单独解释,因此您仍然可以弄乱代码;)

如果您使用未定义的函数,您甚至不会期望代码运行,也会出现该错误,例如

function [] = test()
if false
    a = @(x)x;
end
a(1)
end
Run Code Online (Sandbox Code Playgroud)

脚本存储命令行条目,即编译器别无选择,只能单独处理每一行(您可能想将其视为键盘宏)。相反,函数是封装的代码段。编译器(通常)不期望任何未知的东西+它认为这段封装的代码可能会被重用。因此,它确保事先将所有代码编译一次(如果编译会一直这样做,则称为提前编译)。

当您清除两者之间的变量时,这变得特别明显:

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

(请注意,clear清除所有变量,但clear all也会清除删除代码(请参阅MATLAB 执行引擎))

看看Loren 的这篇有趣的博客文章

MATLAB 通过即时或即时编译 MATLAB 代码,提供了两全其美的优势。无论是在类、函数、脚本中还是仅在命令行中,MATLAB 代码都会被编译。没有明确的编译步骤供用户启动,并且 MATLAB 代码可以在一次只有一行的块中执行。MATLAB JIT 编译器生成本机机器级代码,这些代码针对正在执行的 MATLAB 代码和特定硬件平台进行了优化。

无论如何,您不应在代码中编写死胡同或覆盖(本机)函数。使用函数句柄来克服这个问题是很好的,但请确保为所有情况定义它

function [] = test()
if false % dead end definition
    fnc = @(x)x;
else
    fnc = @plot;
end
fnc(1)
end
Run Code Online (Sandbox Code Playgroud)