精简版:
作为第四个参数传递的函数accumarray
有时会被调用,其参数与编写第一个参数的规范不一致accumarray
.
因此,用作参数的函数accumarray
必须测试实际上是异常条件的内容.
问题是:如何对这种异常条件进行1表达式匿名函数测试?更一般地说:如何编写对accumarray
未记录的行为具有鲁棒性的匿名函数?
完整版本:
下面的代码是我今天大部分工作日的问题的彻底提炼版本.
首先是一些定义:
idxs = [1:3 1:3 1:3]';
vals0 = [1 4 6 3 5 7 6 Inf 2]';
vals1 = [1 Inf 6 3 5 7 6 4 2]';
anon = @(x) max(x(~isinf(x)));
Run Code Online (Sandbox Code Playgroud)
注释vals1
是vals0
通过交换元素2和8获得的."匿名"函数anon
计算其输入的非无限元素中的最大值.
鉴于这些定义,下面两个调用
accumarray(idxs, vals0, [], anon)
accumarray(idxs, vals1, [], anon)
Run Code Online (Sandbox Code Playgroud)
其区别仅在于在其第二个参数(vals0
VS vals1
)时,应产生相同的结果,因为之间的差vals0
和vals1
只影响在该参数的值与所述呼叫的一个排序anon
,并且该函数的结果是不敏感的排序其论点中的元素.
事实证明,这两个表达式中的第一个正常评估并产生正确的结果1:
>> accumarray(idxs, vals0, [], anon)
ans =
6
5
7
Run Code Online (Sandbox Code Playgroud)
然而,第二个失败的是:
>> accumarray(idxs, vals1, [], anon)
Error using accumarray
The function '@(x)max(x(~isinf(x)))' returned a non-scalar value.
Run Code Online (Sandbox Code Playgroud)
要解决这个问题,我只能拿出2是写一个单独的函数(在它自己的文件,当然,"MATLAB的方式")
function out = kluge(x)
global ncalls;
ncalls = ncalls + 1;
y = ~isinf(x);
if any(y)
out = max(x(y));
else
{ncalls x}
out = NaN;
end
end
Run Code Online (Sandbox Code Playgroud)
...并运行以下内容:
>> global ncalls;
>> ncalls = int8(0); accumarray(idxs, vals0, [], @kluge)
ans =
6
5
7
>> ncalls = int8(0); accumarray(idxs, vals1, [], @kluge)
ans =
[2] [Inf]
ans =
6
5
7
Run Code Online (Sandbox Code Playgroud)
从上一次调用的输出可以看出,accumarray
第二次调用kluge
回调的参数是数组[Int]
.这毫无疑问地告诉我,accumarray
它不像文档3那样表现(因为idxs
指定长度为1的数组不会被传递给accumarray
函数参数).
事实上,从这个和其他测试我确定,与我的预期相反,传递给的函数accumarray
被称为超过max(idxs)
(= 3)次; 在涉及kluge
上面的表达式中,它被称为5次.
这里的问题是,如果一个人不能依赖于如何accumarray
实际调用函数参数,那么使这个函数参数健壮的唯一方法是在其中包含许多额外的代码来执行必要的检查.这几乎肯定会要求函数有多个语句,这些语句排除了匿名函数.(例如,kluge
上面的函数比健壮的更强大anon
,但我不知道如何适应匿名函数.)不能使用匿名函数会accumarray
大大降低其实用性.
所以我的问题是:
如何指定可以作为健壮参数的匿名函数?
accumarray
1我在本文所示的所有MATLAB输出中删除了MATLAB典型的过填充空白行.
2我欢迎您提出任何其他故障排除建议的评论; 解决这个问题要比它应该困难得多.
3
特别是,请参阅"函数按如下方式处理输入:"行后面的项目编号1到5 .
第四输入参数accumarray
,anon
在这种情况下,必须返回一个标量进行任何输入.
在索引排序时考虑输出:
>> [idxsSorted,sortInds] = sort(idxs)
>> accumarray(idxsSorted, vals0(sortInds), [], anon)
ans =
6
5
7
>> accumarray(idxsSorted, vals1(sortInds), [], anon)
ans =
6
5
7
Run Code Online (Sandbox Code Playgroud)
现在,所有文档都要说明如下:
如果subs中的下标未排序,则fun应该不依赖于其输入数据中的值的顺序.
这与麻烦有anon
什么关系?这是一个线索,因为正如Luis Mendo所建议的那样,强制anon
要求给定给定的整个值idx
集而不是子集/子阵列.
考虑如何accumarray
处理未排序的索引和值列表:
>> [idxs vals0 vals1]
ans =
1 1 1
2 4 Inf
3 6 6
1 3 3
2 5 5
3 7 7
1 6 6
2 Inf 4
3 2 2
Run Code Online (Sandbox Code Playgroud)
对于这两个vals0
和vals1
中,Inf
属于其中一组idxs
等于2.由于idxs
没有排序,它并不处理所有值idxs=2
一个炮打响,在第一.实际的算法(实现)是不透明的,但它似乎从假设idxs
排序开始,处理第一个参数的每个单值块.这可以通过在fun
第四个输入参数的函数引用中放入断点来验证.当它遇到以1 idxs
为第二时,它似乎开始了,但后续调用fun
包含给定的索引所有的值.可能会将accumarray
某些实现调用unique
为完全分段idxs
(顺便说一句,顺序不会被保留).正如kjo建议的那样,这是accumarray
实际处理输入的点,如文档中所述,遵循此处的步骤1-5("找出有多少个唯一索引......").结果,它vals1
在anon(Inf)
被调用时崩溃,但不是为了vals0
,而是anon(4)
在第一次尝试时调用.
然而,即使它在第一次完全遵循这些步骤,如果仅包含Inf
s 的完整子数组(考虑到anon([Inf Inf Inf]
)也返回空矩阵,则它不一定是健壮的.这是一个fun
必须返回标量的要求,尽管是低调的.文档中不清楚的是,它必须为任何输入返回标量,而不仅仅是根据算法的高级描述返回预期的标量.
解决方法:
anon = @(x) max([x(~isinf(x));-Inf]);
Run Code Online (Sandbox Code Playgroud)
该文档不说,anon
被称为仅与整个组1的vals
对应于每个值idx
作为其输入.如您的示例所示,它会使用其子集进行调用.
因此,建立anon
健壮的方法似乎是:当输入是任何子集vals
(或者可能只是具有相同idx
值的每个集合的任何子集)时,确保它给出标量输出.在你的情况下,anon(inf)
不返回标量.
1当然,它实际上是一个数组,但我认为用集合(和子集)来描述它更容易.