我可以花数小时讲述这个问题,但仍然没有对Matlab签名处理有一个好的整体观。但是这里有一些建议。
首先,对于验证输入类型,采取放任自流的方法。相信调用者。如果您真的想要强类型测试,则需要像Java这样的静态语言。试图在Matlab中到处执行类型安全性,并且您最终将会得到大量的LOC和执行时间,用于运行时类型测试和用户空间中的强制转换,从而牺牲了Matlab的很多功能和开发速度。我是通过吃亏才学会这个道理的。
对于API签名(旨在从其他函数而不是从命令行调用的函数),考虑使用单个Args参数而不是varargin。然后它可以在多个参数之间传递,而无需将其转换为逗号分隔的列表以进行varargin签名。像Jonas所说的那样,结构非常方便。结构和n-by-2 {name,value;...} cells之间存在着很好的同构性,您可以设置几个函数来在您的函数内部将它们转换成所需的格式。
function example(args)
无论您使用inputParser还是自己编写名称/值解析器,都应该将其打包在一个单独的标准函数中,并从具有名称/值签名的函数顶部调用它。让它接受默认值列表,并将其放入方便编写的数据结构中,您的参数解析调用将类似于函数签名声明,这有助于可读性并避免复制和粘贴样板代码。
以下是解析调用的示例。
function out = my_example_function(varargin)
args = parsemyargs(varargin, {
'Stations' {'ORD','SFO','LGA'}
'Reading' 'Min Temp'
'FromDate' '1/1/2000'
'ToDate' today
'Units' 'deg. C'
});
fprintf('\nArgs:\n');
disp(args);
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);
这里有一个实现名称/值解析的函数。你可以空出它并用inputParser、自己的类型约定等替换它。我认为n-by-2单元格约定使源代码易读;考虑保留它。在接收代码中,结构体通常更方便处理,但使用表达式和字面量构造n-by-2单元格更加方便。(结构体需要在每行使用",..."来继续,同时防止单元格值扩展为非标量结构体。)
function out = parsemyargs(args, defaults)
args = structify(args);
defaults = parse_defaults(defaults);
out = merge_args(args, defaults);
function out = parse_defaults(x)
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
if size(x,1) == 1 && size(x,2) > 3
x = reshape(x, [numel(x)/2 2]);
end
if size(x,2) < 2
x(:,2) = {[]};
end
if size(x,2) < 3
x(:,3) = {[]};
end
out = x;
function out = structify(x)
if isempty(x)
out = struct;
elseif iscell(x)
if (size(x,1) == 1) && size(x,2) > 2
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
if ~iscellstr(x(:,1))
error('Invalid names in name/val argument list');
end
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]));
names = fieldnames(args);
for i = 1:numel(names)
out.(names{i}) = args.(names{i});
end
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)
switch type
case 'cellstr'
isThatType = iscellstr(value);
case 'datenum'
isThatType = isnumeric(value);
otherwise
isThatType = isa(value, type);
end
if isThatType
out = value;
else
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
很遗憾,在Matlab中,字符串和日期数不是一流类型。