Car*_*oft 2 matlab types function return-value cell-array
这是一个普遍问题,与特定操作无关.我希望能够将任意函数的结果写入单元格数组的元素,而不考虑函数返回的数据类型.考虑这个伪代码:
zout = cell(n,m);
myfunc = str2func('inputname'); %assume myfunc puts out m values to match zout dimensions
zout(1,:) = myfunc(x,y);
Run Code Online (Sandbox Code Playgroud)
这将适用于"inputname"=="strcat",例如,给定它x
并且y
是具有适当维度的字符串或单元格.但是如果"inputname"=="strcmp"则输出是逻辑数组,并且Matlab会抛出错误.我需要这样做
zout(1,:) = num2cell(strcmp(x,y));
Run Code Online (Sandbox Code Playgroud)
所以我的问题是:有没有办法填充单元格数zout
而不必测试生成的变量类型myfunc(x,y
?我应该首先使用a struct
(如果是这样,最好的方法是填充它)?
(我通常是R用户,我可以在list
没有任何痛苦的情况下使用变量)
编辑:为了简化整体范围,添加以下"要求":我们现在假设,对于返回多个输出的函数,只需要捕获第一个zout
.但是当此输出是N值的向量或单元的向量(即Nx1单元阵列)时,这些N值将被映射到zout(1,1:N)
.
所以我的问题是:有没有办法填充单元格数组zout而不必测试myfunc(x,y)生成的变量类型?我应该首先使用结构(如果是,那么填充它的最佳方法是什么)?
@NotBoStyf提供的答案几乎就在那里,但并不完全.细胞阵列是正确的方法.但是,答案很大程度上取决于函数的输出数量.
该函数strcmp
只有一个输出,即一个数组.原因
zout{1,:} = strcmp(x,y)
Run Code Online (Sandbox Code Playgroud)
当zout的尺寸为N x 2时,会给出一条错误消息,即左侧(zout{1,:}
)需要右侧的两个输出.你可以解决这个问题:
[zout{1,:}] = num2cell(strcmp(x,y)); % notice the square brackets on the LHS
Run Code Online (Sandbox Code Playgroud)
但是,没有理由这样做.您可以简单地定义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);
Run Code Online (Sandbox Code Playgroud)
还有一个案例需要考虑......
如果您的函数产生多个输出(而不是作为数组的单个输出),那么这将仅捕获第一个输出.产生多个输出的函数定义如下:
function [outA,outB] = do_something( a, b )
outA = a + 1;
outB = b + 2;
end
Run Code Online (Sandbox Code Playgroud)
在这里,您需要显式捕获两个输出参数.否则,你就得到了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]
Run Code Online (Sandbox Code Playgroud)
这也可以通过编程方式完成,但有一些限制.假设我们给出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
Run Code Online (Sandbox Code Playgroud)
这种方法有一些注意事项:
它捕获所有输出.这并不总是你想要的.
捕获所有输出通常可以改变函数的行为.例如,find
如果仅使用一个输出,则函数返回线性索引;如果使用两个输出,则返回行/列索引;如果使用三个输出,则返回行/列/值.
它不适用于具有可变输出数量的函数.这些功能定义为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
Run Code Online (Sandbox Code Playgroud)
要将输出解压缩为2-D单元阵列,每个输出的大小必须是常量.假设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
Run Code Online (Sandbox Code Playgroud)
然后输出可以作为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
Run Code Online (Sandbox Code Playgroud)
然后可以将输出作为outp{i}{j}
,并且输出的大小可以变化.
要记住以下几点:
Matlab单元基本上是效率低下的指针.JIT编译器并不总是优化它们以及数值数组.
将数字数组拆分成单元可能会花费相当多的内存.每个拆分值实际上是一个数字数组,其中包含与之关联的大小和类型信息.在数字数组形式中,每个数组都会出现一次.当数组被拆分时,每个元素都会产生一次.