用 {} 操作符包装函数是否可以替代在 cellfun 中使用 'UniformOutput', false?

16

我正在使用 cellfun 来将一个函数应用到一个单元格数组中的每个单元格。

我知道,当函数返回非标量值时,必须将 '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后,我意识到如果我像这样使用cell array construction operator {}包装函数@(x) {x + 1},那么我就不需要将'UniformOutput'设置为false了。

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

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

在代码布局方面,我更喜欢这种方法,因为它比以前的方法更紧凑,更简洁,但我不确定这是否总是安全的。
因此我的问题是:
我是否总是可以用{}来包装函数,以避免设置'UniformOutput'false?或者有任何情况下这种替换不起作用吗?

我的研究:

help cellfun

'UniformOutput' -- 一个逻辑值,指示是否可以在不封装到单元格数组中的情况下返回FUN的输出。如果为true(默认值),则FUN必须返回可连接成数组的标量值。如果为true,则输出必须为以下类型:数字、逻辑、字符、结构体、单元格。如果为falsecellfun将返回一个单元格数组(或多个单元格数组),其中(I,J,...)th单元格包含值FUN(C{I,J,...}, ...)。当'UniformOutput'false时,输出可以是任何类型。

以下片段是相关问题的答案的一部分:

[...] cellfun负责解除引用操作,该操作在循环时需要对单个单元格的各个元素进行详细操作(即{})[...]

2个回答

16

我可以想到两种情况,其中使用 单元封装 而不是额外参数 ...'UniformOutput', false) 会导致问题,第一种情况比第二种更有可能发生:

  • Capturing multiple outputs: Sometimes you may want to capture multiple outputs from the function you are applying, such as calling unique on each element of your cell array and getting the additional index arguments. Using ...'UniformOutput', false) we can easily do this:

    >> 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]
    

    However, this fails when using cell encapsulation:

    >> [C2, index] = cellfun(@(x) {unique(x)}, C1)
    Output argument "varargout{2}" (and maybe others) not assigned during call to
    "@(x){unique(x)}".
    
  • Functions with no output: I know what you're thinking: "But if they produce no output, then I don't have to worry about collecting non-scalar values!" True, but I'm imagining an admittedly unusual scenario where the function to be evaluated may be an additional parameter, like a function handle passed as an argument, so you don't know a priori how many outputs you'll be dealing with. Regardless, the two approaches differ for such a case:

    >> 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)}
    

    Probably not something you'll ever have to worry about, but I thought I'd include it for the sake of completeness.


哇..我不知道用cellfun捕获多个输出甚至是可能的。这是一个启发性的答案!谢谢 :) - codeaviator

2

从您的问题中可以得到的答案是"不能"总是使用 {} 来避免设置 "UniformOutput" 为 false。我相信匿名函数无法成功的主要原因是它缺乏类型检查,而程序员对函数返回类型了解甚少。

实际上,在我的观点中,你不应该只纠结于在匿名函数中是否使用 cell 索引运算符 {} 来包装你的返回值。更好的做法是在调用 cellfun 后确定返回值类型,或在调用 cellfun 前检查输入值类型。当然,在你的函数句柄中实现类型检查程序或者 try...catch 也可以确保你完全掌控你的返回类型。老实说,在 Matlab 中知道你发送什么和将要接收什么非常重要。

简而言之,除非你有意创建一个作为返回类型的 cell 数组,并且你已经知道从你调用的函数中得到的返回值可以用来创建一个 cell 数组,否则你不应该使用 {} 来封装你的匿名函数。

如果你使用的是2016b或更高版本,则可以在脚本中创建一个函数句柄,而无需创建新文件。详情请参见 脚本中的函数


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接