2

这是一个普遍问题,与特定操作无关。我希望能够将任意函数的结果写入元胞数组的元素,而不考虑函数返回的数据类型。考虑这个伪代码:

zout = cell(n,m);
myfunc = str2func('inputname'); %assume myfunc puts out m values to match zout dimensions
zout(1,:) = myfunc(x,y);

这将适用于 "inputname" == "strcat" ,例如,假设xandy是具有适当维度的字符串或字符串单元格。但是如果 "inputname" == "strcmp" 那么输出是一个逻辑数组,并且 Matlab 会抛出一个错误。我需要做

zout(1,:) = num2cell(strcmp(x,y));

所以我的问题是:有没有办法填充单元格数组zout而无需测试生成的变量类型myfunc(x,y ?我应该首先使用 astruct吗(如果是,填充它的最佳方法是什么)?
(我通常是 R 用户,我可以轻松使用list变量)

编辑:为了简化整体范围,添加以下“要求”:我们现在假设,对于返回多个输出的函数,只需要在zout. 但是当这个输出是 N 个值的向量或单元格的向量(即 Nx1 单元格数组)时,这些 N 个值被映射到zout(1,1:N).

4

2 回答 2

5

所以我的问题是:有没有一种方法可以填充元胞数组 zout 而无需测试 myfunc(x,y) 生成的变量类型?我应该首先使用结构(如果是,填充它的最佳方法是什么)?

@NotBoStyf 提供的答案几乎就在那里,但并不完全。元胞数组是正确的方法。然而,答案很大程度上取决于函数的输出数量。

只有一个输出的函数

该函数strcmp只有一个输出,即一个数组。原因是

zout{1,:} = strcmp(x,y)

给你一个错误信息,当 zout 的尺寸为 N x 2 时,左侧 ( zout{1,:}) 期望右侧有两个输出。您可以使用以下方法解决此问题:

[zout{1,:}] = num2cell(strcmp(x,y));  % notice the square brackets on the LHS

但是,真的没有理由这样做。您可以简单地定义zout为 N x 1 元胞数组并捕获结果:

zout = cell(1,1);

x = 'a';
y = { 'a', 'b' };

zout{1} = strcmp(x,y);

% Referring to the results:
x_is_y_1 = zout{1}(1);
x_is_y_2 = zout{1}(2);

还有一种情况要考虑...

具有多个输出的函数

如果您的函数产生多个输出(而不是作为数组的单个输出),那么这只会捕获第一个输出。产生多个输出的函数定义如下:

function [outA,outB] = do_something( a, b )
  outA = a + 1;
  outB = b + 2;
end

在这里,您需要显式捕获两个输出参数。否则,你只会得到a. 例如:

outA = do_something( [1,2,3], [4,5,6] ); % outA is [2,3,4]

[outA,outB] = do_something( [1,2,3], [4,5,6] ); % outA is [2,3,4], outB is [6,7,8]

Z1 = cell(1,1);
Z1{1,1} = do_something( [1,2,3], [4,5,6] ); % Z1{1,1} is [2,3,4]

Z2 = cell(1,2);
Z2{1,1:2} = do_something( [1,2,3], [4,5,6] ); % Same error as above.  
% NB: You really never want to have a cell expansion that is not surrounded 
% by square brackets.

% Do this instead:
[Z2{1,1:2}] = do_something( [1,2,3], [4,5,6] ); % Z2{1,1} is [2,3,4], Z2{1,2} is [6,7,8]

这也可以通过编程方式完成,但有一些限制。假设我们有一个函数 func,它接受一个输入并返回一个恒定(但未知)数量的输出。我们有一个单元格数组inp,其中包含我们要处理的输入,我们希望将结果收集到周围的单元格中outp

N = numel(inp);
M = nargout(@func);  % number of outputs produced by func 
outp = cell(N,M);
for i=1:N
  [ outp{i,:} ] = func( inp{i} );
end

这种方法有一些注意事项:

  1. 它捕获所有输出。这并不总是你想要的。

  2. 捕获所有输出通常可以改变函数的行为。例如,find如果只使用一个输出,则该函数返回线性索引,如果使用两个输出,则返回行/列索引,如果使用三个输出,则返回行/列/值。

  3. 它不适用于具有可变数量输出的函数。这些函数定义为function [a,b,...,varargout] = func( ... )nargout如果函数已在其输出列表中声明,则将返回负数varargout,因为 Matlab 无法知道将产生多少输出。

将数组和单元格输出解包到一个单元格中

到目前为止一切都是真的,但是:我希望的是一个通用的解决方案。如果函数产生单元格输出,我不能使用 num2cell 。因此,对 strcmp 有效的方法对 strcat 无效,反之亦然。现在让我们假设,对于一个返回多个输出的函数,只需要在 zout 中捕获第一个输出 – Carl Witthoft

要为返回单元格或数组的所有函数提供统一的输出语法,请使用适配器函数。这是一个处理数值数组和单元格的示例:

function [cellOut] = cellify(input)
  if iscell(input)
    cellOut = input;
  elseif isnumeric(input)
    cellOut = num2cell(input);
  else
    error('cellify currently does not support structs or objects');
  end
end

要将输出解包到二维元胞数组中,每个输出的大小必须是恒定的。假设M输出:

N = numel(inp);
% M is known and constant
outp = cell(N,M);
for i=1:N
  outp(i,:) = cellify( func( inp{i} ) );  % NB: parentheses instead of curlies on LHS
end

然后可以将输出寻址为outp{i,j}。另一种方法允许输出的大小变化:

N = numel(inp);
% M is not necessary here
outp = cell(N,1);
for i=1:N
  outp{i} = cellify( func( inp{i} ) );  % NB: back to curlies on LHS
end

然后可以将输出寻址为outp{i}{j},并且输出的大小可以变化。

要记住几件事:

  1. Matlab 单元基本上是低效的指针。JIT 编译器并不总是像数值数组一样优化它们。

  2. 将数值数组拆分为单元格会占用大量内存。每个拆分值实际上是一个数值数组,具有与其相关的大小和类型信息。在数值数组形式中,每个数组都会发生一次。拆分数组时,每个元素都会发生一次。

于 2012-06-29T15:16:38.207 回答
0

赋值时使用花括号代替。使用

 zout{1,:} = strcmp(x,y);

相反应该工作。

于 2012-06-26T14:27:33.550 回答