mcpField
M
matlab-validate-function-arguments

matlab-validate-function-arguments

github.com/matlab开发1.2k84 次安装1 天前更新
代码仓库

关于 matlab-validate-function-arguments

这是面向 MATLAB 编码 Agent 的参数验证技能,涵盖 arguments 块的语义、组合规则和常见误区,适用于编写、审查及迁移函数参数验证。

主要功能

  1. 01arguments 块尺寸与类型语义
  2. 02重复参数和输出参数规则
  3. 03名称-值参数及属性导入
  4. 04从 inputParser 等迁移
  5. 05识别隐式转换风险

适用场景

  1. 01为 MATLAB 函数添加参数验证
  2. 02审查函数签名的类型与尺寸行为
  3. 03将 inputParser 验证迁移到 arguments 块

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/matlab/matlab-agentic-toolkit --skill matlab-validate-function-arguments

安装后在 Claude Code 中输入/matlab-validate-function-arguments确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

manifest.yaml
references/
migration-guide.md
validators-reference.md
references/examples/
dot-question-syntax.md
repeating-args.md

详细介绍

MATLAB 函数参数验证

使用语义正确的 arguments 块编写健壮的 MATLAB 函数,正确处理大小、类、重复参数和属性导入。

适用场景

  • 编写带有 arguments 块的函数
  • 使用重复参数(arguments (Repeating))
  • 编写接受名称-值参数的类构造函数
  • 从 inputParser 或 validateattributes 迁移
  • 检查函数签名中可能导致隐式转换的问题
  • 在类规格、mustBeA 和验证器之间进行选择

不适用场景

  • 不涉及参数验证的基本 MATLAB 编程
  • 应用构建或 UI 组件(请使用 matlab-build-app)
  • 单元测试(请使用 matlab-write-tests)
  • 与参数验证无关的一般 OOP 类设计
  • 函数体内部用于运行时不变量检查的简单 if/error 守卫语句 (这些不属于输入验证——除非用户明确要求添加 arguments 块,否则保持原样)
  • .mlx(Live Script)或 .mlapp(App Designer)文件——这些是二进制 ZIP 容器。不要解压,也不要尝试进行结构性编辑。如果用户要求在其中的函数里 添加 arguments 块,请让他们先导出为纯 .m 文件,或在 MATLAB 中打开文件并在那里编辑。

常见误解

以下是 Agent 常犯的错误。请先阅读这些内容。

大小规格会重塑,而不是拒绝输入

错误认知: (1,:) 会在遇到列向量时触发错误。

实际行为: MATLAB 会静默重塑输入,使其符合声明的大小。

function out = myFunc(x)
    arguments
        x (1,:) double
    end
    out = x;
end

myFunc([1; 2; 3])  % Does NOT error! Returns [1 2 3] (reshaped to row)

将列向量 [1;2;3] 传入 (1,:) 后,会变成行向量 [1 2 3]。若要真正拒绝列向量,请使用验证器:

function out = myFunc(x)
    arguments
        x {mustBeNumeric, mustBeRow}
    end
    out = x;
end
类规格会转换,而不是拒绝输入

错误认知: 在 arguments 块中指定 double 会拒绝非 double 输入。

实际行为: MATLAB 会尝试将输入隐式转换为声明的类。

function out = myFunc(x)
    arguments
        x double
    end
    out = x;
end

myFunc('hello')  % Does NOT error! Returns [104 101 108 108 111] (ASCII codes)
myFunc(single(3.14))  % Does NOT error! Returns double(3.14)

若要在不进行转换的情况下拒绝输入,请使用 mustBeA 或 mustBeFloat:

x {mustBeA(x, "double")}           % Rejects single, char, int32, etc.
x {mustBeFloat}                     % Accepts single OR double, rejects char/int
x {mustBeNumeric}                   % Accepts any numeric, rejects char/string
数值验证器不会拒绝复数

错误认知: mustBeNumeric、mustBeFinite 和 mustBeInteger 会拒绝 1+2i 这类复数输入。

实际行为: 复数属于数值、有限值;如果其实部和虚部都是整数值,也属于整数——因此 1+2i 会在不报错的情况下通过这三项验证。大小、索引、计数和大多数物理标量都应明确拒绝 复数:

function H = hilb2(n)
    arguments
        n (1,1) {mustBeInteger, mustBePositive, mustBeReal}
    end
    H = 1./((1:n)' + (0:n-1));
end

如果复数输入不符合函数的约定,请添加 mustBeReal。

计算默认值使用传入值,而不是声明的默认值

错误认知: 默认值表达式会使用前面参数声明的默认值。

实际行为: 默认值表达式会根据实际传入的值进行计算。

function out = myFunc(fs, windowSize)
    arguments
        fs (1,1) double {mustBePositive}
        windowSize (1,1) double {mustBePositive} = round(fs / 10)
    end
    out = windowSize;
end

myFunc(8000)      % windowSize = round(8000/10) = 800 (uses passed fs)
myFunc(8000, 256) % windowSize = 256 (explicitly passed)

这样就不需要使用哨兵值([])并在验证后再修正。

~ 是有效的占位符——不要重命名

错误认知: arguments 块中的名称是必填项,因此函数签名中的 ~ 占位符必须重命名为 obj/this 等。

实际行为: ~ 可以作为参数块中的名称(R2019b+)。请保留它。

function normalize(~, results)
    arguments
        ~
        results (1,:) struct
    end
    ...
end

如果方法签名使用 ~ 标记未使用的输入(例如未使用的类实例),请在 arguments 块中保留 ~:

  • 不要为 ~ 指定类或大小规格——像 (1,1) MetricCalculator 这样的类型规格需要名称,因此添加规格会迫使你重命名。请完全省略规格。
  • 不要添加 %#ok<INUSL> / %#ok<INUSA> / %#ok<MANU> 指令——~ 已经告诉 lint 检查过程该输入是有意未使用的。
  • 不要为了“加强验证”而将 ~ 重命名为 obj。重命名会重新引入原来的 ~ 所抑制的 lint 警告,却不会带来任何约定上的好处。

模式

重复参数

对于接受可变参数组的函数(例如 plot(x1,y1,x2,y2,...)),请使用 arguments (Repeating)。函数体中的每个已声明变量都会变成一个元胞数组。

function plotMultiSeries(x, y)
    arguments (Repeating)
        x (1,:) double {mustBeFinite}
        y (1,:) double {mustBeFinite}
    end

    figure;
    hold on;
    for i = 1:numel(x)
        plot(x{i}, y{i});
    end
    hold off;
end

关键规则:

  • 变量会变成元胞数组——使用 x{i} 访问,而不是 x(i)
  • 组中的所有变量会一起重复(完整的参数组)
  • 重复参数不能有默认值
  • 重复零次也是有效的(会生成空元胞数组)
输出参数验证 — arguments (Output) 和 (Output, Repeating)

R2022b+。 早期版本不支持输出参数验证 — 输入验证(本技能的大部分内容)从 R2019b 起可用。

使用 arguments (Output, Repeating) 声明输出数量随 nargout 变化的函数。该块必须恰好声明一个名称 — MATLAB 会拒绝包含多个名称的重复输出块,并报错 MATLAB:functionValidation:MultipleRepeatingOutputs。若要返回相关值的组(例如三元组),请将其打包到单个输出中,并让调用方请求 N * groupSize 个输出。

function out = productTriples()
    arguments (Output, Repeating)
        out (1,1) double
    end
    out = cell(1, nargout);
    for k = 1:3:nargout
        b = rand;
        c = rand;
        out{k}   = b * c;
        out{k+1} = b;
        out{k+2} = c;
    end
end

调用方:[p1, a1, b1, p2, a2, b2] = productTriples();

关键规则:

  • 只能有一个名称 — arguments (Output, Repeating) a; b; c; end 会在解析时出错
  • 输出会被赋值为一个 1×nargout 元胞数组;调用位置的逗号分隔列表展开会将其内容分配给请求的 LHS 变量
  • 对单个已声明名称设置的验证器适用于每个元胞元素
  • nargout 会告知函数体请求的输出数量;零个也是有效的
组合规则 — 哪些内容可以共存

一个函数可以有多个 arguments 块。以下规则适用于整个函数 — 所有参数类别在各个块中合起来必须按此顺序出现:

required → optional (with defaults) → repeating → name-value

在任何位置违反顺序 — 即使跨越不同的块 — 都会导致解析失败。不要凭直觉判断:以下组合都可以在同一个函数中合法组合:

  • 必需参数 + 可选参数 + 重复参数 + 名称-值参数(也允许 .?ClassName)
  • 必需参数 + 重复参数 + 名称-值参数
  • 重复参数 + 名称-值参数
  • (Output, Repeating) 加上任意组合的输入块
  • (Repeating) 块中的 ~ 占位符

数量限制:

块类型允许数量
普通 arguments(位置参数和/或名称-值参数)多个,按顺序排列
arguments (Repeating)(输入)每个函数恰好一个
arguments (Output, Repeating)每个函数恰好一个
普通块中的名称-值结构体多个 — 结构体名称须不同

解析时错误标识符 — 如有疑问,请编写函数并让 MATLAB 告诉你。这些是组合规则可能产生的唯一错误;错误名称会明确说明对应规则:

标识符含义
MATLAB:functionValidation:RequiredAfterOptional必需参数出现在带默认值的参数之后
MATLAB:functionValidation:OptionalAfterRepeating可选参数/位置参数出现在重复参数块之后
MATLAB:functionValidation:PositionalAfterNamed位置参数或重复参数出现在名称-值参数之后
MATLAB:functionValidation:MultipleRepeatingBlocks存在多个 (Repeating) 块(输入或输出)
MATLAB:functionValidation:RepeatingHasDefault(Repeating) 参数设置了默认值

如果不确定某种组合是否合法,不要凭空编造限制 — 编写一个最简示例并运行。错误标识符(或没有错误)就是答案。

最复杂的示例

单个函数包含必需参数 + 可选参数 + 忽略参数 + 重复参数 + 忽略的重复参数 + 名值参数 + 类导入的名值参数(这里是 SensorConfig——在上文 .?ClassName 小节中定义的类):

function maximal(req, opt, ~, x, ~, options, classArg)
    arguments
        req (1,1) double
        opt (1,1) double = 7
        ~
    end
    arguments (Repeating)
        x (1,1) double
        ~
    end
    arguments
        options.Title (1,1) string = "default"
        options.Verbose (1,1) logical = false
        classArg.?SensorConfig
    end
    % function body
end

调用:maximal(1, 2, 3, 4, 'a', 5, 'b', Title="t", SampleRate=44100) — 可解析并运行。SampleRate 是通过 .? 行从 SensorConfig 导入的名值参数之一。

不要为 .?ClassName 选择没有公共可设置属性的类——例如,onCleanup 只有 task(私有设置),所以 .?onCleanup 不会导入任何名值参数。该行在语法上有效,但不会产生任何效果;调用方针对它编写的任何“名值”都会被视为未知名称——请参阅「常见错误」中的“未知的 Name=Value 会被静默忽略”。

有关组合重复参数与名值选项,请参阅上文的最复杂示例和 references/examples/repeating-args.md。

.?ClassName — 将属性导入为名值参数

在构造函数中使用 .?ClassName,可直接根据属性定义生成名值参数。这样就无需在参数块中重复声明属性。

有关其他模式——覆盖特定属性、带转发功能的静态工厂,以及包装图形类属性——请参阅 references/examples/dot-question-syntax.md。

classdef SensorConfig
    properties
        SampleRate (1,1) double {mustBePositive} = 1000
        Resolution (1,1) double {mustBePositive, mustBeInteger} = 16
        FilterOrder (1,1) double {mustBePositive, mustBeInteger} = 4
        Label (1,1) string = "unnamed"
    end

    methods
        function obj = SensorConfig(nvArgs)
            arguments
                nvArgs.?SensorConfig
            end
            props = fieldnames(nvArgs);
            for i = 1:numel(props)
                obj.(props{i}) = nvArgs.(props{i});
            end
        end
    end
end

优点:

  • 无需重复定义大小、类、验证器或默认值
  • 添加或移除属性时,构造函数会保持同步
  • 自动补全会显示所有可设置属性
namedargs2cell — 转发名值参数

使用 namedargs2cell 将经过验证的结构体转换回名值元胞数组,以便转发给其他函数:

function obj = fromPreset(presetName, nvArgs)
    arguments
        presetName (1,1) string
        nvArgs.?SensorConfig
    end

    switch presetName
        case "audio"
            defaults = struct(SampleRate=44100, Resolution=24, Label="audio");
        case "vibration"
            defaults = struct(SampleRate=10000, Resolution=16, Label="vibration");
    end

    % Apply overrides
    overrides = fieldnames(nvArgs);
    for i = 1:numel(overrides)
        defaults.(overrides{i}) = nvArgs.(overrides{i});
    end

    args = namedargs2cell(defaults);
    obj = SensorConfig(args{:});
end

迁移决策框架

从 inputParser 或 validateattributes 迁移时,可使用此决策树处理类:

原始检查功能参数块中的等效写法原因
@isnumeric接受任意数值,不进行转换{mustBeNumeric}不指定类——避免转换
@ischar只接受 char{mustBeA(label, 'char')} 或 (1,:) charmustBeText/mustBeTextScalar 会扩大接受范围,允许 string
@islogical只接受 logical(1,1) {mustBeA(x, 'logical')}logical 类规范会进行转换——logical(1) → true,因此在 @islogical 会拒绝 1/0(以及其他可转换的标量)的情况下,这种写法会接受它们。使用 mustBeA(不指定类)可在不转换的情况下拒绝这些值。
{'numeric'}接受任意数值,不进行转换{mustBeNumeric}validateattributes 元类——与 @isnumeric 接受的集合相同
{'float'}接受 single 或 double{mustBeFloat}等效于 {'single','double'}
{'integer'}(类规范,少见)只接受整数类型(int8…uint64)通常使用 {mustBeNumeric, mustBeInteger}。仅在确有意图时严格限制为整数类型:{mustBeA(x, ["int8","int16","int32","int64","uint8","uint16","uint32","uint64"])}仅指定类的形式会拒绝 5.0 这类整数值 double——这通常是意外结果。除非原代码明确意图拒绝 double,否则应改用限制较宽松的组合。
{'numeric'}, {'integer'}(类 + 属性)数值且取值为整数{mustBeNumeric, mustBeInteger}常见组合——同时接受 int 类型和整数值浮点数
{'single','double'}限定为浮点类型{mustBeFloat}double 类规范会将 single 转换为 double
{'double'} 精确匹配拒绝 single、int、char{mustBeA(x, "double")}double 类规范会进行转换,而非拒绝
@(x) isa(x,'MyClass')类成员关系{mustBeA(x, "MyClass")}验证但不进行转换

**核心原则:**如果原代码会拒绝类型不匹配的值,请使用验证器(mustBeA、mustBeFloat、mustBeNumeric)。如果希望自动转换以方便调用方,请使用类规范。

匿名函数验证器

迁移时不要删掉这些验证器——它们都有内置的等效写法:

inputParser 验证器arguments 块中的等效写法
@(x) ~isempty(x){mustBeNonempty}
@(x) x > 0{mustBePositive}
@(x) x >= 0{mustBeNonnegative}
@(x) x < 0{mustBeNegative}
@(x) x <= 0{mustBeNonpositive}
@(x) isfinite(x){mustBeFinite}
@(x) isreal(x){mustBeReal}
@(x) isnumeric(x) && isreal(x){mustBeNumeric, mustBeReal}
@(x) ismember(x, set){mustBeMember(x, set)}
validatestring(x, set)不要替换为 mustBeMember。 validatestring 会执行不区分大小写的前缀匹配,并返回规范形式;mustBeMember 只进行精确匹配。保留函数体内的调用,并且在 arguments 块中仅使用 {mustBeTextScalar}。

详细的迁移示例请参阅 references/migration-guide.md。

约定

  • 新代码优先使用 string 而非 char(迁移代码除外)
  • 如果 arguments (Repeating) 或名称-值块能够表达相同的接口,绝不要使用 varargin 手动解析
  • 在构造函数中使用 .?ClassName,而不是重新声明属性
  • 如果默认值仅取决于更早的参数,优先使用计算默认值(= expression),而不是使用哨兵值并检查 isempty
  • 不要将 mustBeNumeric 与 double 类规范结合使用来拒绝非数值输入——类规范会在验证器运行前转换 char('A' → 65),导致检查失效。移除类规范:使用 x {mustBeNumeric},而不是 x (1,1) double {mustBeNumeric}
  • 不要将文本验证器与 string 类规范结合使用——类规范会在验证器运行前转换非文本值(例如,42 → "42"),导致检查失效。单独使用文本验证器:使用 p {mustBeTextScalar},而不是 p (1,1) string {mustBeTextScalar}
  • 绝不要使用 varargin + 手动 class() 检查来拒绝类型。 如果 double 这类类规范会转换(而非拒绝)char/single/int 输入,正确的做法是使用验证器(mustBeNumeric、mustBeFloat、mustBeA),而不是退回到 varargin。退回到 varargin 会违背使用 arguments 块的初衷。

常见错误

错误错在哪里正确做法
对重复参数组使用 varargin需要手动解析 30 多行代码arguments (Repeating) — 3 行
在构造函数中重新声明属性重复声明会逐渐不同步nvArgs.?ClassName
认为 (1,:) 会拒绝列向量它会静默地将其重塑使用 mustBeRow 拒绝
认为 double 会拒绝 char在验证器运行前,它会静默地将其转换为 ASCII去掉 double 类规范,仅使用 {mustBeNumeric};或者使用 {mustBeA(x,"double")} 严格检查类
使用 [] 作为计算默认值的哨兵设计过于复杂,还需要自定义验证器使用引用前面参数的 = expression
将 @ischar 替换为 mustBeTextScalar放宽了约束,允许字符串使用 mustBeA(x,'char') 以严格保持原有约束
使用 (1,1) string {mustBeTextScalar}类规范会在验证器运行前将 42 转换为 "42"仅使用 {mustBeTextScalar}(不指定类规范)
将 validatestring 替换为 mustBeMembervalidatestring 会进行不区分大小写的前缀匹配('AU' → 'auto'),并返回规范形式;mustBeMember 只进行精确匹配在函数体中保留 validatestring。仅使用 arguments 块进行类型/大小检查:mode {mustBeTextScalar},然后使用 mode = validatestring(mode, {...})
数值参数省略 mustBeReal复数值会静默通过 mustBeNumeric、mustBeFinite 和 mustBeInteger——1+2i 是有限的数值整数当复数输入不合理时(索引、大小、计数、物理标量),添加 mustBeReal
在 arguments 块中将 ~ 重命名为 obj/this~ 是 arguments 块中的有效占位符名称——重命名会再次触发原先由 ~ 抑制的 lint 警告保留 ~,不加类/大小规范,也不加 %#ok pragma
在 arguments (Output, Repeating) 中声明多个名称MATLAB 会报错 MultipleRepeatingOutputs——只允许一个名称声明单个输出名称;将多个值组成的组打包到该 cell 数组中,并让调用方请求 N * groupSize 个输出
声称可选位置参数与 (Repeating) 互斥它们并不互斥——必需参数、可选参数、重复参数和名称-值参数可以按顺序合法组合请参阅“组合规则”。不确定时,编写函数并根据 MATLAB 的解析时错误标识符(例如 OptionalAfterRepeating、PositionalAfterNamed)判断实际问题所在
认为当函数包含 arguments (Repeating) 时,未知的 Name=Value 会明确报错并不会。存在 (Repeating) 块时,无法识别的 Name=Value 会被静默地吸收为两个位置参数(先是名称标记,再是值)。一个错误的名称-值参数对还会将之前有效的名称-值参数重新归类为位置参数,因此设置选项会恢复为默认值。(没有 (Repeating) 时,相同调用会报错 MATLAB:TooManyInputs。)常见原因之一:对没有公共可设置属性的类(例如 .?onCleanup)使用 .?ClassName——导入时没有暴露任何属性,因此调用方针对该类编写的所有 NV 都无法识别。请选择具有公共可设置属性的类。调试“为什么显示的是我的 NV 默认值?”时,检查调用中是否存在任何无法识别的 Name=——一个错误名称就会污染整个 NV 部分

验证器速查

验证器用途说明
mustBeFloat仅接受单精度或双精度拒绝整数、字符
mustBeA(x, classes)严格检查类,不进行转换mustBeA(x, ["single","double"])
mustBeBetween(x, lo, hi, type)范围检查R2025a 及更高版本。R2025a 之前:使用 mustBeGreaterThan/mustBeLessThan
mustBeNonNan拒绝 NaN 值内置函数,非自定义函数
mustBeMatrix要求为二维(M 行 N 列)R2024b 及更高版本
mustBeRow要求为 1 行 N 列R2024b 及更高版本。与 (1,:) 不同,它会拒绝列向量
mustBeVector接受行向量或列向量方向灵活
mustBeSorted元素按顺序排列R2026a 及更高版本

完整的验证器参考,请参阅 references/validators-reference.md。


版权所有 2026 The MathWorks, Inc.


评论

支持 Markdown