关于 matlab-validate-function-arguments
这是面向 MATLAB 编码 Agent 的参数验证技能,涵盖 arguments 块的语义、组合规则和常见误区,适用于编写、审查及迁移函数参数验证。
主要功能
- 01arguments 块尺寸与类型语义
- 02重复参数和输出参数规则
- 03名称-值参数及属性导入
- 04从 inputParser 等迁移
- 05识别隐式转换风险
适用场景
- 01为 MATLAB 函数添加参数验证
- 02审查函数签名的类型与尺寸行为
- 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.yamlreferences/migration-guide.mdvalidators-reference.mdreferences/examples/dot-question-syntax.mdrepeating-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,:) char | mustBeText/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 替换为 mustBeMember | validatestring 会进行不区分大小写的前缀匹配('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.