Ошибка MATLAB? Ошибка "неопределенная функция или переменная" при использовании одного имени для функции и переменной

иногда удобно использовать функцию в качестве" постоянной " переменной в 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:somepath';
end
load(fullfile(pathConstant, 'filename.ext'));

Я знаю, что решения по языковому дизайну часто сложны и сложны, но одним из наиболее неудачных последствий выбора MATLAB здесь, чтобы игнорировать функцию с тем же именем, является то, что она нарушает совместимость между функциями и скриптами/командной строкой. Например, в скрипте без проблем выполняется следующее:

if false
    a = 'foo';
end
a

где функция a (показано выше) сохраняется в свой файл.

2 ответов


это связано с тем, как Matlab выполняет привязку имен во время компиляции. Потому что matlabBugTest имеет строку, которая присваивает значение a, a определяется как переменная, а более поздняя строка с a является ссылкой на эту переменную, а не вызовом локальной функции. Более современные версии Matlab, такие как моя установка R2015a, дают более четкое сообщение об ошибке:

при компиляции "a" было определено как переменная, и эта переменная неинициализированный. "a" также является именем функции, и предыдущие версии MATLAB назвали бы функция. Однако MATLAB 7 запрещает использование того же имени в том же контексте, что и функция и переменная.

это не столько ошибка, сколько двусмысленность, введенная схемой именования, которой был предоставлен метод разрешения по умолчанию, который может раздражать, если вы никогда не сталкивались с проблемой раньше, и m-lint не отмечает ее. Подобное поведение происходит, когда переменные переносятся в рабочую область без предварительной инициализации.

таким образом, решение состоит в том, чтобы изменить имя функции или переменной на разные вещи, что я бы сказал, что это хорошая практика в любом случае.


рассматривая ваш последующий пример, я заметил некоторое интересное поведение при перемещении вещей в функции. Во-первых, если функция является внешней или вложенной, вы получаете обсуждаемое поведение очень хорошо!-Ответ -24-->по Suever это. Однако, если функция локальна, вы можете обойти ограничение (по крайней мере, вы можете в моих установках 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. Вы не можете определить именованную функцию в командной строке или в файле скрипта; поэтому нет никакой нагрузки на JIT Matlab для правильного и однозначно определите, является ли символ вызовом функции или переменной, поскольку каждая строка выполняется последовательно и не компилируется (помимо определенных блоков кода JIT предназначен для распознавания и оптимизации подобных циклов в скриптах). Теперь о том, почему вышеупомянутое жонглирование декларациями работает, я не совсем уверен, так как он полагается на JIT Matlab, о котором я ничего не знаю (и я не взял класс компилятора, поэтому я даже не мог бы сформировать академическую причину, если бы захотел).


причина, по которой вы получаете это поведение, вероятно, что 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 четко говорится в ответ

это связано с тем, как Matlab выполняет привязку имен во время компиляции. Поскольку matlabBugTest имеет строку, которая присваивает значение a, a определяется как переменная, а более поздняя строка с a является ссылкой на эту переменную, а не вызовом локальной функции

Matlab не поддерживает постоянные выражения и выполняет только ограниченный анализ статического кода. Фактически, если оператор if принимает аргумент false, или enterStatement является ложным, Matlab предоставляет предупреждение,это утверждение (и, возможно, следующие) не может быть достигнуто. Если enterStatement имеет значение false Matlab также генерирует другое предупреждение,переменная a используется, но может быть unset. Однако если enterStatement = mod(0,2), так сказать, если enterStatement вызывает функцию, вы не получите никакого предупреждения вообще. Этот означает,что если пример в вопросе был разрешен, то (c, 2) будет компилироваться на основе того, как оценивалась функция, и это противоречие. Это означает, что код должен компилироваться на основе результатов выполнения.

Примечание: конечно, было бы хорошо, если бы Matlab мог генерировать ошибку в случае enterStatement было выражением вместо константы false, но возможно ли это или нет, это будет зависеть от реализации, я думаю.