在MATLAB中,cellfun是否总是可以用arrayfun替换?

时间:2013-08-14 18:09:07

标签: arrays matlab

我在MATLAB 2007中找到了一个例子,其中cellfunarrayfun几乎可以互换使用:

>> 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甚至需要存在?

3 个答案:

答案 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'}

然而,如果我们想在操作过程中做些什么,那就有点不同了。例如,我们可能想要提取每个字符串的第一个字符。比较cellfunarrayfun的结果:

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',falsecellfun等效的函数,无论是否使用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使用@isemptyarrayfun

至于为什么cellfun仍然存在,我怀疑它是历史的(不破坏向后兼容性)

答案 2 :(得分:0)

cellfun()主要出于历史原因,即至少从1998年开始,而arrayfun()和structfun were introduced仅在2005年末,在R14SP3版本中出现。

另外,正如Nirkhis 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不能是数字数组。