在 cellfun 中,用 {} 运算符包装函数是 'UniformOutput', false 的有效替代吗?

Is wrapping the function with the {} operator a valid replacement of 'UniformOutput', false in cellfun?

我正在使用 cellfun 将函数应用于元胞数组中的每个元胞。

我知道每当函数 returns 非标量值时,我必须将 'UniformOutput' 设置为 false,以便函数的输出封装在元胞数组中返回。

以下面的元胞数组为例:

C1 = {[1 2 3], [4 5 6]};

C1 有两个单元格,每个单元格包含一个包含三个元素的向量:

C1 =

  1×2 cell array

    [1×3 double]    [1×3 double]

如果我想将 1 添加到每个单元格的内容中,我可以定义函数 @(x) x + 1 并使用 cellfun 应用它,如下所示:

C2 = cellfun(@(x) x + 1, C1, 'UniformOutput', false);

这很好用,但请注意,我需要确保 'UniformOutput' 设置为 false,正如我之前解释的那样,否则会抛出错误。

然而,在阅读了 this thread, I realized that if I wrap the function with the cell array construction operator {} 这样的 @(x) {x + 1} 之后,我不需要将 'UniformOutput' 设置为 false

因此以下命令将生成与 C2 中相同的结果而不会引发任何错误:

C3 = cellfun(@(x) {x + 1}, C1);

在代码布局方面,我更喜欢这种方法,因为它比前者更紧凑、更简洁,但我不确定这是否总是安全的。


因此我的问题是:

我可以总是用 {} 包装函数以避免将 'UniformOutput' 设置为 false 吗?或者是否存在这样的替换不起作用的情况?


我的研究:

help cellfun

'UniformOutput' -- a logical value indicating whether or not the output(s) of FUN can be returned without encapsulation in a cell array. If true (the default), FUN must return scalar values that can be concatenated into an array. If true, the outputs must be of the following types: numeric, logical, char, struct, cell. If false, cellfun returns a cell array (or multiple cell arrays), where the (I,J,...)th cell contains the value FUN(C{I,J,...}, ...). When 'UniformOutput' is false, the outputs can be of any type.

以下片段是 an answer 相关问题的一部分:

[...] cellfun takes care of the dereference operation which is required to do detailed operations on individual elements of a cell when looping (that is, the {}) [...]

我可以想到两种使用 cell encapsulation instead of the additional arguments ...'UniformOutput', false) 会导致问题的情况,第一种情况比第二种情况更有可能出现:

  • 捕获多个输出: 有时您可能希望从正在应用的函数中捕获多个输出,例如在每个元素上调用 unique您的元胞数组并获取额外的索引参数。使用 ...'UniformOutput', false) 我们可以轻松做到这一点:

    >> C1 = {[1 2 3], [4 5 6]};
    >> [C2, index] = cellfun(@(x) unique(x), C1, 'UniformOutput', false)
    C2 =
      1×2 cell array
        [1×3 double]    [1×3 double]
    index =
      1×2 cell array
        [3×1 double]    [3×1 double]
    

    但是,当使用单元格封装时,这会失败:

    >> [C2, index] = cellfun(@(x) {unique(x)}, C1)
    Output argument "varargout{2}" (and maybe others) not assigned during call to
    "@(x){unique(x)}".
    
  • 没有输出的函数: 我知道你在想什么:"But if they produce no output, then I don't have to worry about collecting non-scalar values!" 是的,但我正在想象一个公认的不寻常场景,其中要评估的函数可能是一个附加参数,例如作为参数传递的函数句柄,因此您不知道 a priori 有多少你将要处理的输出。无论如何,对于这种情况,这两种方法是不同的:

    >> C1 = {[1 2 3], [4 5 6]};
    >> cellfun(@(x) disp(x), C1, 'UniformOutput', false);
         1     2     3
         4     5     6
    
    >> cellfun(@(x) {disp(x)}, C1);
    Error using disp
    Too many output arguments.
    Error in @(x){disp(x)}
    

    可能您永远不必担心,但我认为为了完整起见我还是将其包括在内。

从你的问题...

Can I always wrap the function with {} to avoid setting 'UniformOutput' to false? Or are there any scenarios where such replacement would not work?

简短的回答是 "NO"。而且我相信你的匿名函数失败的主要原因是因为它缺乏类型检查并且程序员对函数类型知之甚少 returning 类型。

事实上,在我看来,您不应该纠结于使用单元格索引运算符 {} 来包装您的 return 值或不在您的匿名函数中。更好的做法是在调用 cellfun 之后确定 return 值类型,或者在调用 cellfun 之前检查输入值类型。当然,您也可以在 function handle 中实施类型检查程序或 try...catch 以确保对 return 类型的完全控制。老实说,了解您发送的内容和将要接收的内容在 Matlab 中非常重要。

简而言之,您不应该使用 {} 来封装您的匿名函数,除非您有意创建一个元胞数组作为 return 类型,并且您知道 return 您调用的函数中的值可用于创建元胞数组。

仅供参考,如果您有 2016b 或更高版本,而 function in script is possible,您可以在脚本中为 cellfun 创建一个函数句柄,而无需创建新文件。