Je pourrais yack pendant des heures à ce sujet, mais n'ai pas encore une bonne gestalt vue générale Matlab signature de la manipulation. Mais voici quelques conseils.
Tout d'abord, une politique de laissez-faire à valider les types d'entrée. La confiance de l'appelant. Si vous voulez vraiment fort les essais de type, vous voulez un langage statique comme Java. Essayez de faire respecter la sécurité de type, en tous lieux, en Matlab, et vous vous retrouverez avec une bonne partie de votre LOC exécution et de temps consacré à l'exécution des essais de type et de la coercition dans l'espace utilisateur, qui se négocie dans un grand nombre de la puissance et de la vitesse de développement de Matlab. J'ai appris cela à la dure.
Pour l'API de signatures (fonctions destinées à être appelé à d'autres fonctions, au lieu de partir de la ligne de commande), pensez à utiliser un seul argument Args au lieu de mots clé varargin. Ensuite, il peut être passé entre plusieurs arguments sans avoir à le convertir et à partir d'une liste séparée par des virgules pour les mots clé varargin signatures. Les structures, comme Jonas dit, sont très confortables. Il y a aussi une belle isomorphisme entre les structures et n-par-2 {nom,valeur;...} cellules, et vous pourriez mettre en place un couple de fonctions pour convertir entre eux à l'intérieur de vos fonctions à celui qu'on veut l'utiliser en interne.
function example(args)
%EXAMPLE
%
% Where args is a struct or {name,val;...} cell array
Si vous utilisez inputParser ou rouler votre propre nom/val de l'analyseur comme ces autres exemples, l'emballer dans une norme distincte de la fonction que vous allez l'appeler à partir du haut de votre fonctions qui ont nom/val de signatures. Ont-il accepter la valeur par défaut de la liste dans une structure de données qui est pratique pour écrire, et votre arg-analyse des appels ressemble en quelque sorte à la fonction de la signature des déclarations, ce qui contribue à la lisibilité, et d'éviter les copier-coller de code réutilisable.
Voici ce que l'analyse des appels pourrait ressembler.
function out = my_example_function(varargin)
%MY_EXAMPLE_FUNCTION Example function
% No type handling
args = parsemyargs(varargin, {
'Stations' {'ORD','SFO','LGA'}
'Reading' 'Min Temp'
'FromDate' '1/1/2000'
'ToDate' today
'Units' 'deg. C'
});
fprintf('\nArgs:\n');
disp(args);
% With type handling
typed_args = parsemyargs(varargin, {
'Stations' {'ORD','SFO','LGA'} 'cellstr'
'Reading' 'Min Temp' []
'FromDate' '1/1/2000' 'datenum'
'ToDate' today 'datenum'
'Units' 'deg. C' []
});
fprintf('\nWith type handling:\n');
disp(typed_args);
% And now in your function body, you just reference stuff like
% args.Stations
% args.FromDate
Et voici une fonction pour mettre en œuvre le nom/val d'analyse de cette façon. Vous pourriez creuser et de le remplacer avec inputParser, votre propre type de conventions, etc. Je pense que les n-cellules by-2 de la convention rend bien lisible du code source; envisager de garder. Les structures sont généralement plus pratique de travailler avec des dans le code, mais les n-cellules by-2 sont plus faciles à construire en utilisant des expressions et des littéraux. (Les structures exigent le ",..." suite à chaque ligne, et de garder les valeurs de cellule de l'expansion de non scalaires des structures.)
function out = parsemyargs(args, defaults)
%PARSEMYARGS Arg parser helper
%
% out = parsemyargs(Args, Defaults)
%
% Parses name/value argument pairs.
%
% Args is what you pass your varargin in to. It may be
%
% ArgTypes is a list of argument names, default values, and optionally
% argument types for the inputs. It is an n-by-1, n-by-2 or n-by-3 cell in one
% of these forms forms:
% { Name; ... }
% { Name, DefaultValue; ... }
% { Name, DefaultValue, Type; ... }
% You may also pass a struct, which is converted to the first form, or a
% cell row vector containing name/value pairs as
% { Name,DefaultValue, Name,DefaultValue,... }
% Row vectors are only supported because it's unambiguous when the 2-d form
% has at most 3 columns. If there were more columns possible, I think you'd
% have to require the 2-d form because 4-element long vectors would be
% ambiguous as to whether they were on record, or two records with two
% columns omitted.
%
% Returns struct.
%
% This is slow - don't use name/value signatures functions that will called
% in tight loops.
args = structify(args);
defaults = parse_defaults(defaults);
% You could normalize case if you want to. I recommend you don't; it's a runtime cost
% and just one more potential source of inconsistency.
%[args,defaults] = normalize_case_somehow(args, defaults);
out = merge_args(args, defaults);
%%
function out = parse_defaults(x)
%PARSE_DEFAULTS Parse the default arg spec structure
%
% Returns n-by-3 cellrec in form {Name,DefaultValue,Type;...}.
if isstruct(x)
if ~isscalar(x)
error('struct defaults must be scalar');
end
x = [fieldnames(s) struct2cell(s)];
end
if ~iscell(x)
error('invalid defaults');
end
% Allow {name,val, name,val,...} row vectors
% Does not work for the general case of >3 columns in the 2-d form!
if size(x,1) == 1 && size(x,2) > 3
x = reshape(x, [numel(x)/2 2]);
end
% Fill in omitted columns
if size(x,2) < 2
x(:,2) = {[]}; % Make everything default to value []
end
if size(x,2) < 3
x(:,3) = {[]}; % No default type conversion
end
out = x;
%%
function out = structify(x)
%STRUCTIFY Convert a struct or name/value list or record list to struct
if isempty(x)
out = struct;
elseif iscell(x)
% Cells can be {name,val;...} or {name,val,...}
if (size(x,1) == 1) && size(x,2) > 2
% Reshape {name,val, name,val, ... } list to {name,val; ... }
x = reshape(x, [2 numel(x)/2]);
end
if size(x,2) ~= 2
error('Invalid args: cells must be n-by-2 {name,val;...} or vector {name,val,...} list');
end
% Convert {name,val, name,val, ...} list to struct
if ~iscellstr(x(:,1))
error('Invalid names in name/val argument list');
end
% Little trick for building structs from name/vals
% This protects cellstr arguments from expanding into nonscalar structs
x(:,2) = num2cell(x(:,2));
x = x';
x = x(:);
out = struct(x{:});
elseif isstruct(x)
if ~isscalar(x)
error('struct args must be scalar');
end
out = x;
end
%%
function out = merge_args(args, defaults)
out = structify(defaults(:,[1 2]));
% Apply user arguments
% You could normalize case if you wanted, but I avoid it because it's a
% runtime cost and one more chance for inconsistency.
names = fieldnames(args);
for i = 1:numel(names)
out.(names{i}) = args.(names{i});
end
% Check and convert types
for i = 1:size(defaults,1)
[name,defaultVal,type] = defaults{i,:};
if ~isempty(type)
out.(name) = needa(type, out.(name), type);
end
end
%%
function out = needa(type, value, name)
%NEEDA Check that a value is of a given type, and convert if needed
%
% out = needa(type, value)
% HACK to support common 'pseudotypes' that aren't real Matlab types
switch type
case 'cellstr'
isThatType = iscellstr(value);
case 'datenum'
isThatType = isnumeric(value);
otherwise
isThatType = isa(value, type);
end
if isThatType
out = value;
else
% Here you can auto-convert if you're feeling brave. Assumes that the
% conversion constructor form of all type names works.
% Unfortunately this ends up with bad results if you try converting
% between string and number (you get Unicode encoding/decoding). Use
% at your discretion.
% If you don't want to try autoconverting, just throw an error instead,
% with:
% error('Argument %s must be a %s; got a %s', name, type, class(value));
try
out = feval(type, value);
catch err
error('Failed converting argument %s from %s to %s: %s',...
name, class(value), type, err.message);
end
end
Il est donc regrettable que les chaînes et datenums ne sont pas de première classe de types de Matlab.