在MATLAB中使用函数作为“常量”变量的偶尔会很方便。但是当我最近使用此功能时,我遇到了意外错误。当我运行下面的MWE时,我得到错误Undefined function or variable 'a'.
,尽管该功能在同一文件中清晰可用。当我注释掉if
语句时,错误消失了。这似乎意味着MATLAB正在将a
预解释为变量,即使从未到达变量赋值行,忽略了同名函数的事实。这是一个MATLAB错误还是某种想要的行为?
这是MWE:
function matlabBugTest( )
if false
a = 'foo';
end
a
end
function b = a()
b = 'bar';
end
我知道有意为变量和函数使用相同的名称似乎很奇怪,所以我将举例说明这可能有用。例如,您可能希望使用函数来存储某些常量(如文件路径),但也希望能够在找不到该函数时使用不同的值。这种情况可能如下:
if ~exist('pathConstant.m', 'file')
pathConstant = 'C:\some\path';
end
load(fullfile(pathConstant, 'filename.ext'));
我知道语言设计决策通常很困难和复杂,但MATLAB选择忽略同名函数的一个更不幸的后果是它破坏了函数和脚本/命令行之间的兼容性。例如,以下在脚本中运行没有问题:
if false
a = 'foo';
end
a
其中函数a
(如上所示)保存在自己的文件中。
答案 0 :(得分:9)
它与Matlab如何在编译时执行名称绑定有关。由于matlabBugTest
有一行将a
赋值,a
被确定为变量,后一行a
是对该变量的引用而不是调用本地函数。更现代的Matlab版本,比如我的R2015a安装,提供了更明确的错误信息:
在编译时,“a”被确定为变量,并且此变量未初始化。 “a”也是一个函数名称,以前的MATLAB版本会调用它 功能。但是,MATLAB 7禁止在与函数和变量相同的上下文中使用相同的名称。
这不是一个错误,因为命名方案引入的歧义是一种默认的解决方法,如果你之前从未遇到过问题并且m-lint没有标记它,这可能很烦人。如果variables are poofed into the workspace未事先初始化,则会发生类似的行为。
所以解决方案是将函数或变量的名称更改为不同的东西,我认为这是好的做法。
在考虑你的后续例子时,我注意到在函数中移动事物时有一些有趣的行为。首先,如果函数是外部函数或嵌套函数,则可以很好地讨论by Suever's answer的行为。但是,如果函数是本地函数,只要初始化函数或将其显式转换为变量,就可以通过在将函数转换为变量之前调用函数来绕过限制(至少可以在我的R2014b和R2015a安装中)。在某一点。通过这些案例,matlabBugTest
的下列机构如此表现:
失败:
a
if false
a = 'foo';
end
a
运行:
a
if true
a = 'foo';
end
a
运行:
a = a;
if false % runs with true as well.
a = 'foo';
end
a
我不完全确定为什么这种行为是这样的,但显然解析器根据函数的范围和符号出现的顺序以及在什么上下文中处理事物的方式不同。
所以假设这种行为没有而且不会改变,你可以尝试类似的东西:
pathConstant = pathConstant;
if ~exist('pathConstant.m', 'file')
pathConstant = 'C:\some\path';
end
load(fullfile(pathConstant, 'filename.ext'));
虽然,在这里完全是个人观点,但我会做类似
的事情pathConstant = getPathConstant();
if ~exist('pathConstant.m', 'file')
pathConstant = 'C:\some\path';
end
load(fullfile(pathConstant, 'filename.ext'));
关于打破“函数和脚本/命令行之间的兼容性”,我并不认为这是一个问题,因为在Matlab中,这两个是完全不同的上下文。您不能在命令行或脚本文件中定义命名函数;因此,Matlab JIT没有任何负担可以正确无误地确定符号是函数调用还是变量,因为每行都是按顺序执行而不是编译(除了某些代码块,JIT旨在识别和优化脚本中的循环)。现在至于为什么上面的声明工作起作用,我不完全确定,因为它依赖于我一无所知的Matlab JIT(我也没有参加编译器课程,所以如果我不能形成学术上的理由想要的)。
答案 1 :(得分:1)
您获得此行为的原因可能是Matlab从未为其任何语句实现范围解析。请考虑以下代码,
(a)中
if true
a = 'foo';
end
disp(a)
这实际上会显示“foo”。另一方面,
(b)中
if false
a = 'foo';
end
disp(a)
给你错误未定义的函数或变量“a”。那么让我们考虑以下示例,
(C,1)
enterStatement = false;
if enterStatement
a = 'foo';
end
disp(a)
(C,2)
enterStatement = mod(0,2);
if enterStatement
a = 'foo';
end
disp(a)
TroyHaskin在answer
中明确说明了以下内容它与Matlab如何在编译时执行名称绑定有关。因为matlabBugTest有一行为a赋值,所以a被确定为变量,而a后面的行是对该变量的引用而不是对本地函数的调用
Matlab不支持常量表达式,只进行有限的静态代码分析。实际上,如果if语句接受参数false
,或者enterStatement
为假,则Matlab会提供警告,无法访问此语句(可能还有以下语句)。如果enterStatement
设置为false,则Matlab还会生成另一个警告,使用变量a,但可能未设置。但是,如果enterStatement = mod(0,2)
,那么如果enterStatement
调用函数,则根本不会收到任何警告。这意味着如果允许问题中的示例,那么(c,2)将根据函数的评估方式进行编译,这是一个矛盾。这意味着代码必须根据其运行时结果进行编译。
注意:如果enterStatement
是一个表达式而不是常量false
,Matlab可能会产生错误,这可能会很好,但是否这是可能它取决于实施我猜。