Я пришел в собственность кучу кода MATLAB и заметил кучу "волшебных чисел", разбросанных по коду. Как правило, мне нравится создавать эти константы на таких языках, как C, Ruby, PHP и т.д. Когда Googling эта проблема, я обнаружил, что "официальный" способ иметь константы - определять функции, возвращающие постоянное значение. Кажется, kludgey, особенно потому, что MATLAB может быть тонким, если разрешить несколько функций для каждого файла.
Действительно ли это лучший вариант?
Я пытаюсь использовать/сделать что-то вроде препроцессора C, чтобы сделать это для меня. (Я обнаружил, что что-то под названием mpp
было сделано кем-то другим в подобном затруднительном положении, но оно выглядит заброшенным. Код не компилируется, и я не уверен, удовлетворит ли он мои потребности.)
Обычно я просто определяю переменную с UPPER_CASE и размещаю ее в верхней части файла. Но вы должны принять ответственно за то, что не изменили его значение.
В противном случае вы можете использовать классы MATLAB для определения именованных констант.
Теперь Matlab имеет константы. Новый (R2008a +) "classdef" стиль Matlab OOP позволяет определять свойства постоянного класса. Вероятно, это лучший вариант, если вам не нужна обратная совместимость со старыми Matlabs. (Или, наоборот, это хорошая причина отказаться от обратной совместимости.)
Определите их в классе.
classdef MyConstants
properties (Constant = true)
SECONDS_PER_HOUR = 60*60;
DISTANCE_TO_MOON_KM = 384403;
end
end
Затем ссылайтесь на них из любого другого кода с использованием точечной квалификации.
>> disp(MyConstants.SECONDS_PER_HOUR)
3600
Для получения подробной информации см. документацию Matlab для "Объектно-ориентированное программирование" в разделе "Руководство пользователя".
Есть несколько мелких ошибок. Если код случайно пытается записать константу, вместо получения ошибки, она создаст локальную структуру, которая маскирует класс констант.
>> MyConstants.SECONDS_PER_HOUR
ans =
3600
>> MyConstants.SECONDS_PER_HOUR = 42
MyConstants =
SECONDS_PER_HOUR: 42
>> whos
Name Size Bytes Class Attributes
MyConstants 1x1 132 struct
ans 1x1 8 double
Но ущерб является локальным. И если вы хотите быть основательным, вы можете защитить его, вызвав конструктор MyConstants() в начале функции, что заставляет Matlab анализировать его как имя класса в этой области. (ИМХО это слишком много, но это там, если вы этого хотите.)
function broken_constant_use
MyConstants(); % "import" to protect assignment
MyConstants.SECONDS_PER_HOUR = 42 % this bug is a syntax error now
Другой способ заключается в том, что свойства и методы classdef, особенно эта статистика, медленны. На моей машине чтение этой константы примерно на 100 раз медленнее, чем вызов простой функции (22 usec против 0.2 usec, см. этот вопрос). Если вы используете константу внутри цикла, скопируйте ее в локальную переменную перед входом в цикл. Если по какой-то причине вы должны использовать прямой доступ к константам, перейдите к простой функции, которая возвращает значение.
Ради своего здравомыслия, держитесь подальше от препроцессора. Для работы внутри Matlab IDE и отладчика (которые очень полезны) потребуются глубокие и ужасные хаки.
MATLAB не имеет точного const-эквивалента. Я рекомендую НЕ использовать глобальные константы - во-первых, вам нужно убедиться, что они объявлены везде, где вы хотите их использовать. Я бы создал функцию, которая возвращает нужные вам значения. Вы можете проверить этот пост в блоге для некоторых идей.
Возможно, вы найдете некоторые из этих ответов Как мне создать список перечисляемых типов в MATLAB?. Но, короче говоря, нет никакого "однострочного" способа указания переменных, значение которых не должно меняться после начальной настройки в MATLAB.
В любом случае, это все равно будет частью kludge. В прошлых проектах мой подход к этому заключался в том, чтобы определить все константы в качестве глобальных переменных в одном файле script, вызвать script в начале выполнения программы для инициализации переменных и включить "global MYCONST;" в начале любой функции, которая должна была использовать MYCONST. Независимо от того, превосходит ли этот подход "официальный" способ определения функции для возвращения постоянного значения, вопрос о том, что можно спорить в любом случае. Ни один из способов не идеален.
Мой способ иметь дело с константами, которые я хочу передать другим функциям, - это использовать struct:
% Define constants
params.PI = 3.1416;
params.SQRT2 = 1.414;
% Call a function which needs one or more of the constants
myFunction( params );
Он не так чист, как файлы заголовков C, но выполняет эту работу и избегает глобальных MATLAB-глобалов. Если вы хотите, чтобы константы, определенные в отдельном файле (например, getConstants.m), также были бы легкими:
params = getConstants();
Не вызывайте константу с помощью myClass.myconst
, не создавая экземпляр первым! Если скорость не является проблемой. У меня создалось впечатление, что первый вызов свойства константы создаст экземпляр, а затем все будущие вызовы будут ссылаться на этот экземпляр (Свойства с постоянными значениями), но я больше не верю, что это так. Я создал очень базовую тестовую функцию формы:
tic;
for n = 1:N
a = myObj.field;
end
t = toc;
С классами, определенными как:
classdef TestObj
properties
field = 10;
end
end
или
classdef TestHandleObj < handle
properties
field = 10;
end
end
или:
classdef TestConstant
properties (Constant)
field = 10;
end
end
Для разных случаев объектов, объектов-манипуляторов, вложенных объектов и т.д. (а также операций присваивания). Обратите внимание, что это все скаляры; Я не исследовал массивы, ячейки или символы. Для N = 1,000,000 мои результаты (для общего истекшего времени) были:
Access(s) Assign(s) Type of object/call
0.0034 0.0042 'myObj.field'
0.0033 0.0042 'myStruct.field'
0.0034 0.0033 'myVar' //Plain old workspace evaluation
0.0033 0.0042 'myNestedObj.obj.field'
0.1581 0.3066 'myHandleObj.field'
0.1694 0.3124 'myNestedHandleObj.handleObj.field'
29.2161 - 'TestConstant.const' //Call directly to class(supposed to be faster)
0.0034 - 'myTestConstant.const' //Create an instance of TestConstant
0.0051 0.0078 'TestObj > methods' //This calls get and set methods that loop internally
0.1574 0.3053 'TestHandleObj > methods' //get and set methods (internal loop)
Я также создал класс Java и выполнил аналогичный тест:
12.18 17.53 'jObj.field > in matlab for loop'
0.0043 0.0039 'jObj.get and jObj.set loop N times internally'
Накладные расходы при вызове объекта Java высоки, но внутри объекта простые операции доступа и назначения выполняются так же быстро, как обычные объекты matlab. Если вы хотите, чтобы поведение ссылок было загружено, Java может стать способом перехода. Я не исследовал вызовы объектов внутри вложенных функций, но я видел некоторые странные вещи. Кроме того, профилировщик - это мусор, когда дело доходит до многих из этого материала, поэтому я переключился на ручное сохранение времени.
Для справки используется класс Java:
public class JtestObj {
public double field = 10;
public double getMe() {
double N = 1000000;
double val = 0;
for (int i = 1; i < N; i++) {
val = this.field;
}
return val;
}
public void setMe(double val) {
double N = 1000000;
for (int i = 1; i < N; i++){
this.field = val;
}
}
}
В соответствующей заметке здесь приведена ссылка на таблицу констант NIST: таблица ascii и функция matlab, которая возвращает структуру с указанными значения: Matlab FileExchange