我在MATLAB 2007中找到了一个例子,其中cellfun
和arrayfun
几乎可以互换使用:
>> cellfun(@(c) c, {'one' 'two' 'three'}, 'uniformoutput', 0)
% ans =
% 'one' 'two' 'three'
>> arrayfun(@(c) c, {'one' 'two' 'three'})
% ans =
% 'one' 'two' 'three'
我还可以想到arrayfun
有效但cellfun
没有的例子:
>> arrayfun(@(c) c, [1 2 3])
% ans =
% 1 2 3
>> cellfun(@(c) c, [1 2 3])
% ??? Error using ==> cellfun
% Input #2 expected to be a cell array, was double instead.
我的问题是:cellfun
是否有效,但arrayfun
没有?如果是,请举例。如果不是,为什么cellfun
甚至需要存在?
答案 0 :(得分:8)
这很有趣。您的示例正在执行两个不同的操作,这恰好会导致相同的结果。这是一种探索的乐趣。
TL; DR。当您的输入是数组时,通常应该使用arrayfun
;当您的输入是单元格时,通常应使用cellfun
,尽管您通常可以强制arrayfun
执行此任务,但语法地址会有所不同
从根本上说,arrayfun
意味着对数组进行操作,cellfun
意味着对单元格进行操作。但是,Matlab明智地会注意到一个单元格只不过是一组“单元格”,所以arrayfun
无论如何都会起作用。
正如您所指出的,以下两行执行相同的操作:
cellfun(@(c) c, {'one' 'two' 'three'}, 'uniformoutput', 0) %returns {'one' 'two' 'three'}
arrayfun(@(c) c(1), {'one' 'two' 'three'}); %returns {'one' 'two' 'three'}
然而,如果我们想在操作过程中做些什么,那就有点不同了。例如,我们可能想要提取每个字符串的第一个字符。比较cellfun
和arrayfun
的结果:
cellfun( @(c) c(1), {'one' 'two' 'three'}, 'uniformoutput', 0); %returns {'o' 't' 't'}
arrayfun(@(c) c(1), {'one' 'two' 'three'}); %Returns {'one' 'two' 'three'}
使用arrayfun得到相同的结果,我们需要取消引用匿名函数中的单元格,然后提取字符,然后将结果放入单元格数组而不是字符数组。像这样:
arrayfun(@(c) c{1}(1), {'one' 'two' 'three'},'uniformoutput',false) %Returns {'o' 't' 't'}
所以区别在于cellfun
负责解除引用操作,这是在循环时对单元的各个元素进行详细操作所必需的(即{}
),而{{1只执行标准索引(即arrayfun
)。此外,()
表示法确定输出是写入常规arral还是单元数组。
要在代码中显示这意味着什么,请参阅以下与'uniformoutput',false
和cellfun
等效的函数,无论是否使用arrayfun
表示法。除了在循环中使用'uniformoutput',false
与()
之外,这四个函数是等效的:
{}
对于您发布的示例,function out = cellFunEquivalent(fn, x)
for ix = numel(x):-1:1
out(ix) = fn(x{ix});
end
out = reshape(out,size(x));
end
function out = arrayFunEquivalent(fn, x)
for ix = numel(x):-1:1
out(ix) = fn(x(ix));
end
out = reshape(out,size(x));
end
function out = cellFunEquivalent_nonuniform(fn, x)
for ix = numel(x):-1:1
out{ix} = fn(x{ix});
end
out = reshape(out,size(x));
end
function out = arrayFunEquivalent_nonuniform(fn, x)
for ix = numel(x):-1:1
out{ix} = fn(x(ix));
end
out = reshape(out,size(x));
end
函数实际上在单个元素单元格上运行,并将这些单元格的副本重建为同一(单元格)类的另一个数组(请参阅arrayfun
)。 arrayFunEquivalent
操作取消引用输入单元阵列的每个元素,然后将这些字符串的副本重建为单元格数组(请参阅cellfun
)。当输入cellFunEquivalent_nonuniform
是单元格时,这些操作是等效的。
答案 1 :(得分:3)
有一些内置函数可以在cellfun
中按名称引用,但在arrayfun
中不能以相同的方式使用。来自帮助:
A = CELLFUN('fun', C), where 'fun' is one of the following strings,
returns a logical or double array A the elements of which are computed
from those of C as follows:
'isreal' -- true for cells containing a real array, false
otherwise
'isempty' -- true for cells containing an empty array, false
otherwise
'islogical' -- true for cells containing a logical array, false
otherwise
'length' -- the length of the contents of each cell
'ndims' -- the number of dimensions of the contents of each cell
'prodofsize' -- the number of elements of the contents of each cell
因此cellfun('isreal', {'one' 'two' 'three'})
是一个有效的表达式,但使用arrayfun
的任何类似调用都会触发First input must be a function handle
错误。
当然,您可以@isreal
使用@isempty
或arrayfun
至于为什么cellfun
仍然存在,我怀疑它是历史的(不破坏向后兼容性)
答案 2 :(得分:0)
cellfun()
主要出于历史原因,即至少从1998年开始,而arrayfun()
和structfun were introduced仅在2005年末,在R14SP3版本中出现。
另外,正如Nirk中his answer所指出的那样,cellfun()
支持一些遗留语法,但仅限于少数情况,通常比句柄@
更快对手。
阅读两份文件:
[B1,...,Bm] = arrayfun(func,A1,...,An)
...将数组A1,...,An
中的元素传递给函数n
,其中func
是输入数。 ...i
次迭代对应于语法[B1(i),...,Bm(i)] = func(A1{i},...,An{i})
...
[A1,...,Am] = cellfun(func,C1,...,Cn)
调用函数句柄func
指定的函数,并传递单元格数组C1,...,Cn
中的元素...
因此,前者接受数组,而后者仅接受数组。
滥用符号,在您的第一个示例中,根据文档,A1 = C1 = {'one' 'two' 'three'}
是合法的,而在第二个案例中A1 = [1 2 3]
但是C1
不能是数字数组。