3

概括

当使用依赖于参数块中的多个参数的函数参数验证(Repeating)时,当前参数通常传递给验证函数,而其他参数作为部分填充的元胞数组传递。这与非(Repeating)参数块中的工作方式不同。这是预期的行为还是错误?

设想

考虑下面的函数dummy1,它使用自定义参数验证函数 mustBeEqualSize来确保参数xy具有相同的大小。由于 的验证y取决于 的值x,因此我将其称为“交叉参数”验证*

*如果有更好的术语,请评论或编辑。

function dummy1(x, y)
    arguments
       x (1,:) 
       y (1,:) {mustBeEqualSize(x,y)}
    end
    % Do something with x and y.
end

function mustBeEqualSize(a, b)
    % Validates that function arguments have the same size.
    if ~isequal(size(a), size(b))
        eid = 'Size:notEqual';
        msg = "Arguments must have the same size.";
        throwAsCaller(MException(eid, msg))
    end
end

正如所写,这种形式的参数验证按预期工作:

dummy1(1:3, 4:6)  % arguments have same size; validation passes (okay)
dummy1(1:3, 4:7)  % arguments have different size; validation fails (okay)

在这两种情况下,mustBeEqualSize在参数验证期间调用when 时,y两者都作为 1xN 双数组接收,这符合我的期望:ab

% Inside call to mustBeEqualSize(x,y), when x=1:3, y=4:6 in dummy1
a =
     1     2     3
b =
     4     5     6

当被修改为通过添加到参数块dummy1来接受重复参数时,就会出现问题:(Repeating)

function dummy2(x, y)
    arguments (Repeating)
       x (1,:) 
       y (1,:) {mustBeEqualSize(x,y)}
    end
    % Do something with each pair of x-y arguments.
    % In this body, both x and y will be 1xN cell arrays, where N is the
    % number of argument groups passed.
end

现在,当我们调用 时dummy2(1:3, 4:6),参数验证失败。使用调试器,我发现 whenmustBeEqualSize在验证期间被调用ya作为 1x1 单元格数组接收,而b仍然是 1x3 双精度数组:

% Inside call to mustBeEqualSize(x,y), when x=1:3, y=4:6 in dummy2
a =
  1×1 cell array
    {[1 2 3]} 
b =
     4     5     6

当使用更多重复参数时,这个问题更加明显:

dummy2(1:3, 4:6, 1:3, 4:6, 1:3, 4:6)  % 3 argument groups

结果是

a =
  1×3 cell array
    {[1 2 3]}    {0×0 double}    {0×0 double}
b =
     4     5     6

似乎在 in 的验证过程ydummy2y接受了当前正在验证的参数的值,而xMATLAB 分配了一个(部分填充的)单元数组缓冲区来保存所有x传递的 - 参数。

这当然会破坏交叉参数验证,因为只有当前正在验证的参数实际上将自身呈现为单个参数,而其他参数将它们呈现为单元数组缓冲区。

问题

交叉参数验证(Repeating)与非参数的工作方式之间的不匹配是一个错误,还是MATLAB 不支持(Repeating)带有参数的交叉参数验证?(Repeating)如果这种行为差异是意料之中的,有没有办法让交叉参数验证与(Repeating)参数一起工作?

MATLAB 文档对使用重复参数的参数验证进行了以下说明

在函数中,每个重复参数变成一个元胞数组,其元素数等于函数调用中传递的重复次数。验证应用于元胞数组的每个元素。

这似乎没有说明交叉参数验证应该如何与重复参数一起工作。

使用 MATLAB R2021a (9.10.0.1602886) 进行测试。

4

1 回答 1

2

注意:这是我们没有考虑的设计的边缘案例。我已经让相关的开发团队意识到,他们会考虑在未来的版本中修复它。

在短期内,您可以尝试抓取0x0 double单元格数组的最后一个非元素:

function mustBeEqualSize(x,y)
    if iscell(x)
        % Get last non-empty element of `x`
        notEmptyDouble = @(e)isa(e,'double') && ~isequal(size(e), [0, 0]);
        idx = find(cellfun(notEmptyDouble, x), 1, 'last');
        x = x{idx};
    end

    % check equality
    if ~isequal(size(x), size(y))
        error("size mismatch")
    end
end

这将适用于所有非空数组,但不幸的是,鉴于使用的空类型,不适用于dummy2([],[]).

另外,我想指出,无论验证器最终为这个用例工作,考虑如果/当这个问题得到解决时它是否会破坏功能。即,如果此函数需要同时处理双精度数组和元胞数组,将来可能会出现问题。但是,如果此函数只需要双数组,则可以使用

arguments(Repeating)
    a (1,:) double
    b (1,:) double {mustBeEqualSize(x,y)}
end

这与iscell验证器中的检查相结合,应该可以证明此功能的未来。

于 2022-01-27T15:37:38.987 回答