Je suis devenu propriétaire d'un tas de code MATLAB et j'ai remarqué un tas de "nombres magiques" éparpillés sur le code. En général, j'aime bien faire ces constantes dans des langages tels que C, Ruby, PHP, etc. Lorsque j'ai cherché ce problème dans Google, j'ai trouvé que la manière "officielle" de disposer de constantes est de définir des fonctions qui renvoient la valeur constante. Cela semble compliqué, en particulier parce que MATLAB peut être difficile en autorisant plusieurs fonctions par fichier.
Est-ce vraiment la meilleure option?
Je suis tenté d'utiliser/de faire quelque chose comme le préprocesseur C pour le faire pour moi. (J'ai découvert que quelque chose appelé mpp
avait été créé par quelqu'un d'autre dans une situation similaire, mais il semble abandonné. Le code ne compile pas et je ne suis pas sûr que cela puisse répondre à mes besoins.)
En général, je ne fais que définir une variable avec UPPER_CASE et le placer en haut du fichier. Mais vous devez prendre la responsabilité de ne pas changer sa valeur.
Sinon, vous pouvez utiliser les classes MATLAB pour définir des constantes nommées.
Matlab a des constantes maintenant. Le style "classdef" plus récent (R2008a +) de Matlab OOP vous permet de définir des propriétés de classe constantes. C’est probablement la meilleure option si vous n’exigez pas de compatibilité avec les anciens Matlabs. (Ou, inversement, est une bonne raison d’abandonner la rétrocompatibilité.)
Définissez-les dans une classe.
classdef MyConstants
properties (Constant = true)
SECONDS_PER_HOUR = 60*60;
DISTANCE_TO_MOON_KM = 384403;
end
end
Ensuite, faites-les référence à partir de tout autre code en utilisant la qualification de point.
>> disp(MyConstants.SECONDS_PER_HOUR)
3600
Consultez la documentation de Matlab pour "Programmation orientée objet" sous "Guide de l'utilisateur" pour tous les détails.
Il y a quelques pièges mineurs. Si le code tente accidentellement d'écrire dans une constante, au lieu de générer une erreur, il crée une structure locale qui masque la classe des constantes.
>> 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
Mais les dégâts sont locaux. Et si vous voulez être minutieux, vous pouvez vous protéger contre cela en appelant le constructeur MyConstants () au début d'une fonction, ce qui oblige Matlab à l'analyser en tant que nom de classe dans cette étendue. (IMHO c'est exagéré, mais c'est là si vous le voulez.)
function broken_constant_use
MyConstants(); % "import" to protect assignment
MyConstants.SECONDS_PER_HOUR = 42 % this bug is a syntax error now
L’autre acquis est que les propriétés et les méthodes de classdef, en particulier la statique, sont lentes. Sur ma machine, la lecture de cette constante est environ 100 fois plus lente que l’appel d’une fonction simple (22 usec contre 0,2 usec, voir cette question ). Si vous utilisez une constante dans une boucle, copiez-la dans une variable locale avant d'entrer dans la boucle. Si, pour une raison quelconque, vous devez utiliser l'accès direct aux constantes, utilisez une fonction simple qui renvoie la valeur.
Pour des raisons de santé mentale, éloignez-vous du préprocesseur. Pour que cela fonctionne à l'intérieur de Matlab IDE et du débogueur (qui sont très utiles), des hacks profonds et terribles seraient nécessaires.
MATLAB n'a pas d'équivalent const exact. Je recommande de ne PAS utiliser global pour les constantes. D'une part, vous devez vous assurer qu'elles sont déclarées partout où vous souhaitez les utiliser. Je créerais une fonction qui renvoie la ou les valeurs souhaitées. Vous pouvez consulter cet article de blog pour quelques idées.
Vous pourriez avoir certaines de ces réponses Comment créer des types énumérés dans MATLAB? utile. En bref, non, il n’existe pas de méthode "une ligne" pour spécifier des variables dont la valeur ne devrait pas changer après la configuration initiale dans MATLAB.
Quelle que soit la façon dont vous le faites, ce sera toujours un peu un kludge. Dans les projets précédents, mon approche consistait à définir toutes les constantes en tant que variables globales dans un fichier de script, à appeler le script au début de l'exécution du programme pour initialiser les variables et à inclure "global MYCONST;" instructions au début de toute fonction devant utiliser MYCONST. Que cette approche soit ou non supérieure à la manière "officielle" de définir une fonction pour renvoyer une valeur constante est une question d'opinion qui pourrait être discutée de toute façon. Aucune voie n'est idéale.
Ma façon de traiter les constantes que je veux transmettre à d'autres fonctions est d'utiliser une structure:
% Define constants
params.PI = 3.1416;
params.SQRT2 = 1.414;
% Call a function which needs one or more of the constants
myFunction( params );
Ce n'est pas aussi propre que les fichiers d'en-tête C, mais il fait le travail et évite les globales MATLAB. Si vous voulez que les constantes soient toutes définies dans un fichier séparé (par exemple, getConstants.m), ce serait aussi simple:
params = getConstants();
J'utilise un script avec des constantes simples en majuscules et j'inclus le script dans d'autres scripts tr = qui les a créés.
LEFT = 1;
DOWN = 2;
RIGHT = 3; etc.
Cela ne me dérange pas que ces personnes ne soient pas constantes. Si j’écris "LEFT = 3", je serai tout simplement stupide et il n’existera aucun remède contre la stupidité, je ne me dérange donc pas . Mais je déteste vraiment le fait que cette méthode encombre mon espace de travail ne jamais avoir à inspecter. Et je n'aime pas non plus utiliser des mots tels que "tourner (MyConstants.LEFT)" car cela rend les instructions plus longues, comme un zillion de caractères, ce qui rend mon code illisible.
Ce dont j'aurais besoin n'est pas une variable mais une possibilité d'avoir de vraies constantes de pré-compilateur. C'est-à-dire: les chaînes qui sont remplacées par des valeurs juste avant l'exécution du code. C'est ainsi que cela devrait être. Une constante ne devrait pas être une variable. Cela n'a pour but que de rendre votre code plus lisible et maintenable. MathWorks: S'IL VOUS PLAÎT, S'IL VOUS PLAÎT, S'IL VOUS PLAÎT. Cela ne peut pas être si difficile à mettre en œuvre. . .
N'appelez pas une constante en utilisant myClass.myconst
sans créer une instance au préalable! À moins que la vitesse ne soit pas un problème. J'avais l'impression que le premier appel à une propriété constante créerait une instance, puis que tous les appels futurs référenceraient cette instance ( Propriétés avec des valeurs constantes ), mais je ne crois plus que ce soit le cas. J'ai créé une fonction de test très basique du formulaire:
tic;
for n = 1:N
a = myObj.field;
end
t = toc;
Avec des classes définies comme:
classdef TestObj
properties
field = 10;
end
end
ou:
classdef TestHandleObj < handle
properties
field = 10;
end
end
ou:
classdef TestConstant
properties (Constant)
field = 10;
end
end
Pour différents cas d’objets, objets de poignée, objets imbriqués, etc. (ainsi que des opérations d’affectation). Notez que ce sont tous des scalaires; Je n'ai pas enquêté sur les tableaux, les cellules ou les caractères. Pour N = 1 000 000, mes résultats (pour le temps total écoulé) sont les suivants:
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)
J'ai également créé une classe Java et exécuté un test similaire:
12.18 17.53 'jObj.field > in matlab for loop'
0.0043 0.0039 'jObj.get and jObj.set loop N times internally'
Les frais généraux liés à l'appel de l'objet Java sont élevés, mais au sein de l'objet, les opérations d'accès et d'affectation simples se déroulent aussi rapidement que les objets matlab classiques. Si vous voulez que le comportement de référence démarre, Java peut être la solution. Je n'ai pas étudié les appels d'objet dans les fonctions imbriquées, mais j'ai vu des choses étranges. En outre, le profileur fait des bêtises quand il s'agit de beaucoup de choses, c'est pourquoi j'ai opté pour la sauvegarde manuelle des temps.
Pour référence, la classe Java utilisée:
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;
}
}
}
Sur une note connexe, voici un lien vers une table de constantes NIST: table ascii et une fonction matlab qui renvoie une structure avec les valeurs répertoriées suivantes: Matlab FileExchange