【问题标题】:matlab subsref: {} with string argument fails, why?matlab subsref:带有字符串参数的{}失败,为什么?
【发布时间】:2012-02-01 13:53:51
【问题描述】:

在 Mathworks File Exchange 存储库中有一些散列或字典类的实现。我所看到的所有内容都使用括号重载来进行键引用,例如

d = Dict;
d('foo') = 'bar';
y = d('foo');

这似乎是一个合理的界面。但是,如果您想轻松拥有包含其他词典的词典,则最好使用大括号 {} 而不是括号,因为这可以让您绕过 MATLAB(似乎是任意的)语法限制,即多个括号不是允许,但允许多个大括号,即

t{1}{2}{3}  % is legal MATLAB
t(1)(2)(3)  % is not legal MATLAB

因此,如果您希望能够轻松地将字典嵌套在字典中,

dict{'key1'}{'key2'}{'key3'}

as 是 Perl 中的一个常见习语,并且在包括 Python 在内的其他语言中可能并且经常有用,那么除非您想使用 n-1 中间变量来提取字典条目 n 深层,这似乎是一个不错的选择。重写类的 subsref 和 subsasgn 操作以对 {} 执行与以前对 () 相同的操作似乎很容易,并且一切都应该正常工作。

除非我尝试它时没有。

这是我的代码。 (我已经将其简化为最小的情况。这里没有实现实际的字典,每个对象都有一个键和一个值,但这足以说明问题。)

classdef TestBraces < handle

    properties
        % not a full hash table implementation, obviously
        key
        value
    end

    methods(Access = public)


        function val = subsref(obj, ref)
            % Re-implement dot referencing for methods.
            if strcmp(ref(1).type, '.')
                % User trying to access a method             
                % Methods access
                if ismember(ref(1).subs, methods(obj))
                    if length(ref) > 1
                        % Call with args
                        val = obj.(ref(1).subs)(ref(2).subs{:});
                    else
                        % No args
                        val = obj.(ref.subs);
                    end
                    return;
                end                
                % User trying to access something else.
                error(['Reference to non-existant property or method ''' ref.subs '''']);
            end
            switch ref.type
                case '()'
                    error('() indexing not supported.');
                case '{}'
                    theKey = ref.subs{1};
                    if isequal(obj.key, theKey)
                        val = obj.value;
                    else
                        error('key %s not found', theKey);
                    end
                otherwise
                    error('Should never happen')
            end
        end    

        function obj = subsasgn(obj, ref, value)
            %Dict/SUBSASGN  Subscript assignment for Dict objects.
            %
            %  See also: Dict
            %

            if ~strcmp(ref.type,'{}')
                error('() and dot indexing for assignment not supported.');
            end

            % Vectorized calls not supported
            if length(ref.subs) > 1
                error('Dict only supports storing key/value pairs one at a time.');
            end
            theKey = ref.subs{1};
            obj.key = theKey;
            obj.value = value;
        end % subsasgn        
    end     
end

使用此代码,我可以按预期分配:

t = TestBraces;
t{'foo'} = 'bar'

(很明显,t 的默认显示输出中的分配工作。)所以subsasgn 似乎工作正常。

但我无法检索值(subsref 不起作用):

t{'foo'}
??? Error using ==> subsref
Too many output arguments.

错误消息对我来说毫无意义,而且我的 subsref 处理程序的第一个可执行行处的断点从未被命中,所以至少从表面上看,这看起来像是一个 MATLAB 问题,而不是我的代码中的错误。

显然() 括号下标的字符串参数是允许的,因为如果您将代码更改为使用() 而不是{},这将正常工作。 (除非你不能嵌套下标操作,这是练习的对象。)

无论是深入了解我在代码中做错了什么,任何使我正在做的事情不可行的限制,或者嵌套字典的替代实现,都将不胜感激。

【问题讨论】:

    标签: oop matlab dictionary operator-overloading hashtable


    【解决方案1】:

    简短回答,将此方法添加到您的课程中:

    function n = numel(obj, varargin)
        n = 1;
    end
    

    编辑:长答案。

    尽管subsref 的函数签名出现在文档中,它实际上是一个可变参数函数——它可以产生可变数量的输出参数。大括号和点索引都可以产生多个输出,如下所示:

    >> c = {1,2,3,4,5};
    >> [a,b,c] = c{[1 3 5]}
    a =
         1
    b =
         3
    c =
         5
    

    subsref 的预期输出数量取决于索引数组的大小。在这种情况下,索引数组的大小为 3,因此有 3 个输出。

    现在,再看看:

    t{'foo'}
    

    索引数组的大小是多少?另外 3. MATLAB 不在乎您打算将其解释为字符串而不是数组。它只是看到输入的大小为 3,而您的 subsref 一次只能输出 1 个东西。所以,论据不匹配。幸运的是,我们可以通过改变 MATLAB 通过重载numel 来确定预期输出数量的方式来纠正问题。引用自文档链接:

    重要的是要注意 numel 与 重载 subsref 和 subsasgn 函数。在这种情况下 用于大括号和点索引的重载 subsref 函数(如所述 在最后一段中),numel 用于计算 从 subsref 返回的预期输出(nargout)。对于重载的 subsasgn 函数,numel 用于计算预期的数量 要使用 subsasgn 分配的输入 (nargin)。的 nargin 值 重载的 subsasgn 函数是 numel 返回的值加 2 (一个用于分配的变量,一个用于结构 下标数组)。

    作为一个类设计者,你必须确保返回的 n 的值 内置的 numel 函数与类设计一致 那个物体。如果 n 不同于 重载 subsref 函数或重载 subsasgn 的 nargin 函数,那么您需要重载 numel 以返回 n 的值 与类的 subsref 和 subsasgn 函数一致。 否则,MATLAB 在调用这些函数时会产生错误。

    你有它。

    【讨论】:

    • 感谢您的冗长回答。在您第一次回答之后,我猜想这是沿着这些路线的,这澄清了它。太好了,再次感谢。
    • 自 R2015b 起,您应该重载 numArgumentsFromSubscript 函数 - 请参阅 stackoverflow.com/questions/8713730/… 和 Mathworks 页面 mathworks.com/help/matlab/matlab_oop/… 的答案。
    • @nekomatic 您的第一个链接似乎回到了这个问题,我想这是无意的?另外,感谢您的评论!
    • 糟糕,好地方。我不记得我要链接到哪个答案,但在网站上搜索 numArgumentsFromSubscript 会发现 5 个点击,所以它一定是其中之一!
    猜你喜欢
    • 2011-05-27
    • 2011-04-04
    • 2019-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多