【问题标题】:Implementing 'curly' and 'access' "chaining" functions in matlab在matlab中实现'curly'和'access'“链接”函数
【发布时间】:2016-09-21 23:48:42
【问题描述】:

我在 mathworks 博客上阅读了 this article 关于 matlab 中的函数式编程的内容,其中有两个辅助函数:

paren = @(x, varargin) x(varargin{:});
curly = @(x, varargin) x{varargin{:}};

完成三重奏的明显第三个(并与五个字母的主题保持一致)是:

acces = @(x, field) x.(field);

抛开在 matlab 中以这种方式实现链接是否是个好主意的讨论(注意:octave 默认支持链接),paren 似乎运行良好,正如预期的那样;但是,curlyacces 有一个主要缺点;考虑以下代码:

>> C = {1,2,3,4; 2,3,4,5; 3,4,5,6; 4,5,6,7};
>> A = [curly(C, 3, ':')]
A =
     3

即预期的序列生成没有发生。
(请注意,此代码在 Octave 中按预期工作,即A = [3,4,5,6]

同样,acces 在 matlab 中不会产生序列

>> S = [struct('name', 'john'), struct('name', 'jim')];
>> A = {acces(S, 'name')}
A = 
    'john'

(而 Octave 产生预期的 A = {'john', 'jim'}

我知道,在 a. 函数如何在 matlab 和 octave 中返回内容,和/或 b. 如何序列由两种语言的单元格和结构生成。

但是,有没有一种编程方式可以让 matlab 执行上述预期操作?
换句话说,有没有办法像 octave 一样定义返回序列的 curlyacces 函数(匿名函数 :p 的额外奖励)?


PS。我正在寻找的答案不是微不足道的 “要获取多个参数,请使用 varargout” 一个。
PS2。 我在 Matlab 2013b 上对此进行了测试,所以我不知道此行为是否已在以后的版本中“修复”(尽管我非常怀疑)。 在最新的 matlab 在线测试http://matlab.mathworks.com

【问题讨论】:

    标签: matlab octave chaining


    【解决方案1】:

    免责声明:更少的答案和更多的一些随机沉思

    这里的问题是,在 MATLAB 中,单个函数(匿名或其他)无法像点引用和 {} 索引那样返回逗号分隔的列表。

    甚至 MATLAB 执行引用的内部函数也无法这样做:

    subsref(S, substruct('.', 'name'))
    %   john
    
    builtin('_dot', S, 'name')              % Only works pre-2015b
    %   jim
    
    subsref(C, substruct('{}', {3 ':'}))
    %   3
    
    builtin('_brace', C, 3, ':')            % Only works pre-2015b
    %   3
    

    但是单个函数可以在 MATLAB 中做的是返回多个输出。这正是 subsref 和其他内置函数返回您希望的多个值的方式

    S = struct('name', {'john', 'jim'});
    
    [val1, val2] = subsref(S, substruct('.', 'name'));
    [val1, val2] = builtin('_dot', S, 'name');
    
    C = num2cell(magic(3));
    
    [val1, val2, val3] = subsref(C, substruct('{}', {3, ':'}));
    [val1, val2, val3] = builtin('_brace', C, 3, ':');
    

    现在这并不能真正帮助您的辅助匿名函数,因为它需要了解预期的输出数量,而这又取决于输入。

    对于您的 acces 函数,确定输出数量相对简单,因此您可以轻松地执行以下操作:

    [A{1:numel(S)}] = acces(S, 'name');
    

    不幸的是,您不能在匿名函数内部执行此操作,而且除了通过对 cell2mat 的后续调用来包装它之外,也没有简单的方法来获取非单元数组。

    [A{1:numel(S)}] = acces(S, 'name');
    A = cell2mat(A);
    

    可以创建一些匿名函数来执行这些不同的操作,但是它们很混乱。

    access_cell = @(s,n)arrayfun(@(x)acces(x,n), s, 'uniform', 0);
    access_array = @(s,n)arrayfun(@(x)acces(x,n), s, 'uniform', 1);
    

    至于您的curly,您可以改为使用paren 来获取单元格数组的子集作为单元格,然后使用cellfun 循环遍历它以产生结果。

    % This is really just using parenthesis
    curly_sequence_cell = paren;
    
    curly_sequence_array = @(varargin)cell2mat(paren(varargin{:}));
    

    真正的解决方案只是使用一个临时变量,然后使用典型的 MATLAB 语法对其进行索引:)

    S = struct('name', {'john', 'jim'});
    A = {S.name};
    

    【讨论】:

    • 我不知道“_dot”和“_brace”,很高兴知道!也感谢纠正我的结构语法,我没有费心去查,哈哈。不错的解决方法,虽然我猜,这会使可读性比parencurly 更差,这已经足够值得怀疑了......在matlab中没有办法避免那个血腥的临时变量,是否存在:p
    • 不过,我确实想知道 matlab 如何从底层的单元格/结构创建序列,这样它在直接调用时有效,但在使用 subsref 调用时无效。当我尝试 subsref 并且它不起作用时,我感到非常惊讶......直到那时我的印象是 () 和 {} 索引只是 subsref 的语法糖......(例如,在 octave 上,subsref,再次,按预期工作并返回一个序列)
    • @TasosPapastylianou 对subsref 进行了更多探索,并在我的答案中添加了一些信息。虽然您可以强制 subsref 为您提供所有值,但仍然很难得到一个逗号分隔的列表。
    • 感谢@Suever!我想这和它在matlab上的距离一样接近!虽然[A{:}] = curly(C, 3,':') 是一个相当不错的解决方法!
    • @TasosPapastylianou 如果你只想要这样的单元格,只需使用 paren 代替:A = paren(C, 3, ':')
    猜你喜欢
    • 2021-11-25
    • 2021-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-29
    • 2013-07-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多