2

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

4

3 回答 3

9

这是有趣的。您的示例正在执行两种不同的操作,这恰好导致相同的结果。探索起来很有趣。

TL;博士。arrayfun当您的输入是一个数组,并且当您的输入是一个单元格时,您通常应该使用cellfun,尽管您通常可以强制arrayfun执行这项工作,并使用不同级别的语法地狱。

从根本上说,arrayfun它旨在对阵列进行操作,并且cellfun旨在对单元进行操作。但是,Matlab-wise 会注意到一个单元只不过是一个“单元”数组,所以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负责在循环时对单元格的各个元素执行详细操作所需的取消引用操作(即{}),而arrayfun只执行标准索引(即())。此外,'uniformoutput',false符号确定输出是否写入常规阵列或单元阵列。

要显示这在代码中的含义,请参阅以下等效于cellfunand的函数arrayfun,无论有无'uniformoutput',false符号。除了在循环中使用()vs.之外,这四个函数是等价的:{}

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)。当输入x是一个单元格时,这些操作是等价的。

于 2013-08-14T18:59:54.840 回答
4

有一些内置函数可以在中按名称引用,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错误。

当然,你可以只使用@isrealor @isemptyforarrayfun

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

于 2013-08-14T18:18:54.113 回答
0

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

此外,正如Nirk他的回答中指出的那样,cellfun()支持一些遗留语法,但仅限于少数情况,这通常比处理@交易对手更快。

从两个文档中读取:

[B1,...,Bm] = arrayfun(func,A1,...,An)...将数组中的元素A1,...,An(其中n是输入的数量)传递给函数func。...第ith 次迭代对应于语法[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不能是数字数组。

于 2013-08-14T19:45:40.863 回答