为了得到健壮的代码,我们非常有必要对函数的参数进行检查,除了最基础的参数个数,还需要关注参数的类型和数据范围等,下面介绍几个MATLAB提供的用于参数检查的内置函数。

validateattributes

validateattributes函数可以为我们检查某个具体参数的类型以及是否满足某些要求,不满足要求会抛出错误。

假设我们有一个函数func1,它接受两个输入参数ab,我们要求:a是一个非空的标量,b是一个非空的向量。

1
2
3
4
5
6
7
function result = func1(a, b)
validateattributes(a, {'numeric'}, {'nonempty', 'scalar'});
validateattributes(b, {'numeric'}, {'nonempty', 'vector'});

result = a + sum(b);
disp(['Result: ', num2str(result)]);
end

validateattributes函数可以确保a是一个非空的标量,b是一个非空的向量。如果输入参数不符合这些要求,MATLAB 会抛出错误。

validateattributes函数的基本格式为

1
validateattributes(a, classes, attributes);

其中:

  • 第一个参数是要检查的函数参数
  • 第二个参数是要求的类型,通常包括:doublelogicalcharstruct
  • 第三个参数是要求参数满足的条件,可以是多个条件,例如{'nonempty', 'vector'},也可以留空{}

关于参数的类型,MATLAB提供了class函数来获取,常见的类型包括:

  • double 浮点数
  • numeric 数值类型(包括各种浮点数和各种整数)
  • char 字符
  • logical 布尔值
  • struct 结构体数组
  • cell 元胞数组
  • function_handle 函数句柄
  • class_name 自定义类型名称

关于参数满足的条件,常见的条件包括

  • 维度检查
    • 2d 二维数组;3d 三维数组
    • row 行向量;column 列向量;vector 行向量或列向量
    • scalar 标量
    • 'size',[d1,...dN] 指定维数信息的数组
    • 'numel',N 指定元素总个数为N的数组
    • 'nrows',N 指定行数为N的数组;'ncols',N 指定列数为N的数组
    • square 方阵(每一个维度都相等)
    • nonempty 要求数组的每一个维度都不为0
    • nonsparse 要求数组非稀疏
  • 大小范围检查
    • '>',N 所有值大于N
    • '>=',N 所有值大于等于N
    • ...
  • 其它检查
    • finite 数组中的元素不含有Inf
    • nonnan 数组中的元素不含有Nan
    • nonnegative 数组中的元素全部非负
    • nonzero 数组中的元素全部非零
    • decreasing 单调减
    • increasing 单调增

validatestring

对于字符串参数,MATLAB专门提供了检查工具validatestring,它可以限制字符串参数的所有合法取值,例如

1
2
3
4
5
6
7
8
9
10
11
12
function result = selectOption(option)
option = validatestring(option, {'Option1', 'Option2', 'Option3'});

switch option
case 'Option1'
result = 'You selected Option 1';
case 'Option2'
result = 'You selected Option 2';
case 'Option3'
result = 'You selected Option 3';
end
end

但是这种限制不是严格的,正如很多MATLAB内置函数一样,可以忽略大小写进行模糊匹配,也可以只匹配到合法选项的开头部分。 如果这个参数只能匹配到唯一的合法选项,就不会报错,反之则会报错。

成功的模糊匹配例如

1
2
3
4
5
6
>> validatestring('G',{'green','red'})
ans =
'green'
>> validatestring('bla',{'black','blue'})
ans =
'black'

下面的匹配则会报错

1
2
3
4
5
6
7
8
>> validatestring('u',{'black','blue'})
Expected input to match one of these values:
'black', 'blue'
The input, 'u', did not match any of the valid values.
>> validatestring('ue',{'black','blue'})
Expected input to match one of these values:
'black', 'blue'
The input, 'ue', did not match any of the valid values.

inputParser

inputParser是输入参数解析器类型,和C++中常见的命令行参数解析器非常类似,只不过在这里是对函数参数进行检查。

假设我们有一个函数func2,它接受两个必需的输入参数ab,一个可选的输入参数c,以及一个键值对参数Verbose

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function result = func2(a, b, varargin)
% 创建一个 inputParser 对象
p = inputParser;
% 配置必需参数
p.addRequired('a', @isnumeric);
p.addRequired('b', @isnumeric);
% 配置可选参数(默认值1)
p.addOptional('c', 1, @isnumeric);
% 配置键值对参数(键值对的值默认取false)
p.addParameter('Verbose', false, @islogical);
% 解析输入参数
p.parse(a, b, varargin{:});
% 重新获取解析后的参数
a = p.Results.a;
b = p.Results.b;
c = p.Results.c;
verbose = p.Results.Verbose;

result = a + sum(b) + c;

if verbose
disp(['a: ', num2str(a)]);
disp(['b: ', num2str(b)]);
disp(['c: ', num2str(c)]);
disp(['Result: ', num2str(result)]);
end
end

在这个示例中,我们首先创建了一个inputParser对象 p,然后用它对参数进行检查:

  • 使用addRequired方法添加了必需参数ab,并指定它们必须是数值类型。
  • 使用addOptional方法添加了可选参数c,并指定默认值为 1。
  • 使用addParameter方法添加了键值对参数Verbose,指定键的名称为Verbose,值必须是布尔类型,默认为false
  • 使用parse方法解析输入参数。
  • p.Results重新获取解析后的参数。

使用例如

1
2
3
4
5
6
7
8
9
10
11
% 调用 func2 并传递必需参数 a 和 b
func2(1, [2, 3]);

% 调用 func2 并传递必需参数 a 和 b 以及可选参数 c
func2(1, [2, 3], 4);

% 调用 func2 并传递必需参数 a 和 b,以及名称-值对参数 'Verbose'
func2(1, [2, 3], 'Verbose', true);

% 调用 func2 并传递必需参数 a 和 b, 可选参数 c 和名称-值对参数 'Verbose'
func2(1, [2, 3], 4, 'Verbose', true);

我们可以使用匿名函数的方式,将validateattributes函数也组合到inputParser中使用,用于判断参数的有效性,例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function result = myFunction(a, b, varargin)
% 创建一个 inputParser 对象
p = inputParser;
% 设置输入参数保持顺序
p.KeepUnmatched = true;
% 必需参数
addRequired(p, 'a', @(x) validateattributes(x, {'numeric'}, {'scalar', 'nonnegative'}));
addRequired(p, 'b', @(x) validateattributes(x, {'numeric'}, {'vector'}));
% 可选参数
addOptional(p, 'c', 1, @(x) validateattributes(x, {'numeric'}, {'scalar', 'nonnegative'}));
% 名称-值对参数
addParameter(p, 'Verbose', false, @(x) validateattributes(x, {'logical'}, {'scalar'}));
% 解析输入参数
parse(p, a, b, varargin{:});
% 重新获取解析后的参数
a = p.Results.a;
b = p.Results.b;
c = p.Results.c;
verbose = p.Results.Verbose;

result = a + sum(b) + c;

if verbose
disp(['a: ', num2str(a)]);
disp(['b: ', num2str(b)]);
disp(['c: ', num2str(c)]);
disp(['Result: ', num2str(result)]);
end
end