【问题标题】:Generalization of mat2str to cell arraysmat2str 到元胞数组的泛化
【发布时间】:2016-11-27 23:06:37
【问题描述】:

我有时会错过生成(可能是嵌套的)元胞数组的字符串表示形式的函数。这将是mat2str 的泛化,仅适用于非元胞数组(数字、字符或逻辑类型)。

给定一个数组x,如何获得一个字符串表示y,以便评估这个字符串产生x

例如输入

x = {[10 20], {'abc'; false; true;}};

应该产生一个类似的输出字符串

y = '{[10 20], {''abc''; false; true}}';

(或关于分隔分隔符的一些变化),这样

isequal(x, eval(y))

true

【问题讨论】:

    标签: arrays string matlab octave cell-array


    【解决方案1】:

    此过程将数据结构转换为稍后可以评估的字符串,名为serialization

    有一个serialize function for Octave 可用于此目的,它支持具有任意维数(不仅是 2d)的任何核心数据类型(不仅是元胞数组)。

    例子:

    ## Works for normal 2d numeric arrays
    octave> a = magic (4);
    octave> serialize (a)
    ans = double([16 2 3 13;5 11 10 8;9 7 6 12;4 14 15 1])
    octave> assert (eval (serialize (a)), a)
    
    ## Works for normal 3d numeric arrays with all precision
    octave> a = rand (3, 3, 3);
    octave> serialize (a)
    ans = cat(3,double([0.53837757395682650507 0.41720691649633284692 0.66860079620859769189;0.018390655109800025518 0.56538265981533797344 0.20709955358395887304;0.86811365238275806089 0.18398187533949311723 0.20280927116918162634]),double([0.40869259684132724919 0.96877003954154328191 0.32138458265911834522;0.37357584261201565168 0.69925333907961184643 0.10937000120952171389;0.3804633375950405294 0.32942660641033155722 0.79302478034566603604]),double([0.44879474273802461015 0.78659287316710135851 0.49078191654039543534;0.66470978375890155121 0.87740365914996953922 0.77817214018098579409;0.51361398808500036139 0.75508941052835898411 0.70283088935085502591]))
    octave> assert (eval (serialize (a)), a)
    
    ## Works for 3 dimensional cell arrays of strings
    octave> a = reshape ({'foo', 'bar' 'qux', 'lol', 'baz', 'hello', 'there', 'octave'}, [2 2 2])
    a = {2x2x2 Cell Array}
    octave> serialize (a)
    ans = cat(3,{["foo"],["qux"];["bar"],["lol"]},{["baz"],["there"];["hello"],["octave"]})
    octave> assert (eval (serialize (a)), a)
    

    不过,更好的问题是why do you want to do this in the first place?如果您这样做的原因是在多个 Octave 实例之间发送变量,请考虑使用具有专门为此目的设计的函数的 parallelmpi 包。

    【讨论】:

    • 谢谢!我不知道这存在于 Octave 中。它支持多维数组!我只是想用它来更好地控制单元格数组的显示方式,主要用于code golf
    • @LuisMendo 我刚刚在#octave 上与安迪(序列化函数的作者)讨论了对他的函数的一些可能的改进。您可以创建一维向量,然后创建reshape,而不是嵌套调用cat。原因是 reshape 是一个非常便宜的操作,而多个 cat 会导致大量的复制开销。
    【解决方案2】:

    以下函数适用于任意数组、任何嵌套结构和任何形状的数组,只要它们都是二维数组即可。不支持多维数组(同mat2str)。

    该函数还允许为元胞数组指定任意行和列分隔符(例如,在逗号和空格之间进行选择),并且还可以选择强制非元胞数组使用这些分隔符(从而覆盖mat2str'behaviour)。元胞数组中的默认分隔符是 ' ' 用于列,'; ' 用于行。

    function y = array2str(x, col_sep, row_sep, sep_noncell)
    % Converts a (possibly cell, nested) array to string representation
    %
    % Optional inputs col_sep and row_sep specify separators for the cell arrays.
    % They can be arbitrary strings (but they should be chosen as per Matlab rules
    % so that the output string evaluates to the input). Optional flag sep_noncell
    % can be used to force those separators with non-cell arrays too, instead of
    % the separators produced by mat2str (space and semicolon)
    
    % Default values
    if nargin<4
        sep_noncell = false;
    end
    if nargin<3
        row_sep = '; ';
    end
    if nargin<2
        col_sep = ' ';
    end
    
    x = {x}; % this is to initiallize processing
    y = {[]}; % [] indicates content unknown yet: we need to go on
    done = false;
    while ~done
        done = true; % tentatively
        for n = 1:numel(y);
            if isempty(y{n}) % we need to go deeper
                done = false;
                if ~iscell(x{1}) % we've reached ground
                    s = mat2str(x{1}); % final content
                    if sep_noncell % replace mat2str's separators if required
                        s = regexprep(s,'(?<=^[^'']*(''[^'']*'')*[^'']*) ', col_sep);
                        s = regexprep(s,'(?<=^[^'']*(''[^'']*'')*[^'']*);', row_sep);
                    end
                    y{n} = s; % put final content...
                    x(1) = []; % ...and remove from x
                else % advance one level
                    str = ['{' repmat([{[]}, col_sep], 1, numel(x{1})) '}'];
                    ind_sep = find(cellfun(@(t) isequal(t, col_sep), str));
                    if ~isempty(ind_sep)
                        str(ind_sep(end)) = []; % remove last column separator
                        ind_sep(end) = [];
                    end
                    step_sep = size(x{1}, 2);
                    str(ind_sep(step_sep:step_sep:end)) = {row_sep};
                    y = [y(1:n-1) str y(n+1:end)]; % mark for further processing...
                    x = [reshape(x{1}.', 1, []) x(2:end)]; % ...and unbox x{1},
                        % transposed and linearized
                end
            end
        end
    end
    y = [y{:}]; % concatenate all strings
    

    上面的函数使用正则表达式来强制在非单元格数组中指定分隔符。由于受支持的后向模式的限制,这在 Matlab 中有效,但在 Octave 中无效。下面的修改版本避免了正则表达式,因此可以在 Matlab 和 Octave 中使用。只有if sep_noncell 和匹配的end 之间的部分相对于第一个版本发生了变化。

    function y = array2str(x, col_sep, row_sep, sep_noncell)
    % Converts a (possibly cell, nested) array to string representation.
    % Octave-friendly version
    %
    % Optional inputs col_sep and row_sep specify separators for the cell arrays.
    % They can be arbitrary strings (but they should be chosen as per Matlab rules
    % so that the output string evaluates to the input). Optional flag sep_noncell
    % can be used to force those separators with non-cell arrays too, instead of
    % the separators produced by mat2str (space and semicolon)
    
    % Default values
    if nargin<4
        sep_noncell = false;
    end
    if nargin<3
        row_sep = '; ';
    end
    if nargin<2
        col_sep = ' ';
    end
    
    x = {x}; % this is to initiallize processing
    y = {[]}; % [] indicates content unknown yet: we need to go on
    done = false;
    while ~done
        done = true; % tentatively
        for n = 1:numel(y);
            if isempty(y{n}) % we need to go deeper
                done = false;
                if ~iscell(x{1}) % we've reached ground
                    s = mat2str(x{1}); % final content
                    if sep_noncell % replace mat2str's separators if required
                        for k = flip(find(~mod(cumsum(s==''''),2) & s==' ')) % process
                            % backwards, because indices to the right will become invalid
                            s = [s(1:k-1) col_sep s(k+1:end)];
                        end
                        for k = flip(find(~mod(cumsum(s==''''),2) & s==';'))
                            s = [s(1:k-1) row_sep s(k+1:end)];
                        end
                    end
                    y{n} = s; % put final content...
                    x(1) = []; % ...and remove from x
                else % advance one level
                    str = ['{' repmat([{[]}, col_sep], 1, numel(x{1})) '}'];
                    ind_sep = find(cellfun(@(t) isequal(t, col_sep), str));
                    if ~isempty(ind_sep)
                        str(ind_sep(end)) = []; % remove last column separator
                        ind_sep(end) = [];
                    end
                    step_sep = size(x{1}, 2);
                    str(ind_sep(step_sep:step_sep:end)) = {row_sep};
                    y = [y(1:n-1) str y(n+1:end)]; % mark for further processing...
                    x = [reshape(x{1}.', 1, []) x(2:end)]; % ...and unbox x{1},
                        % transposed and linearized
                end
            end
        end
    end
    y = [y{:}]; % concatenate all strings
    

    工作原理

    我选择了非递归方法,因为我通常更喜欢迭代而不是递归。

    通过将子字符串或空数组 ([]) 保存在元胞数组 (y) 中逐渐构建输出。 y 单元格中的空数组表示“需要进一步处理”。子字符串定义了“结构”,或者最终定义了单元格嵌套最深层次中的数字、字符或逻辑内容。

    在每次迭代中,y 中找到的第一个空数组将被实际内容替换,或者替换为稍后处理的子字符串和其他空数组。当y不包含任何空数组时,过程结束,将y的所有子字符串串联起来得到最终的字符串输出。

    例如,给定输入x = {[10 20], {'abc'; false; true;}}; 并调用y = array2str(x),每个步骤中的数组y 是一个包含以下内容的元胞数组:

    '{'   []   ', '   []   '}'
    
    '{'   '[10 20]'   ', '   []   '}'
    
    '{'   '[10 20]'   ', '   '{'   []   '; '   []   '; '   []   '}'   '}'
    
    '{'   '[10 20]'   ', '   '{'   ''abc''   '; '   []   '; '   []   '}'   '}'
    
    '{'   '[10 20]'   ', '   '{'   ''abc''   '; '   'false'   '; '   []   '}'   '}'
    
    '{'   '[10 20]'   ', '   '{'   ''abc''   '; '   'false'   '; '   'true'   '}'   '}'
    

    最后将后者拼接成字符串

    '{[10 20] {''abc''; false; true}}'
    

    以自定义分隔符为例,array2str(x, ', ', '; ', true) 会给出

    '{[10, 20], {''abc''; false; true}}'
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-06-18
      • 1970-01-01
      • 1970-01-01
      • 2015-03-01
      • 1970-01-01
      • 2011-07-17
      相关资源
      最近更新 更多