matlab subsref:{}带字符串参数失败,为什么?

jmh*_*mhl 5 oop matlab dictionary hashtable operator-overloading

Mathworks File Exchange存储库中有一些哈希或字典类的实现.我所看到的只是使用括号重载来进行键引用,例如

d = Dict;
d('foo') = 'bar';
y = d('foo');
Run Code Online (Sandbox Code Playgroud)

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

t{1}{2}{3}  % is legal MATLAB
t(1)(2)(3)  % is not legal MATLAB
Run Code Online (Sandbox Code Playgroud)

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

dict{'key1'}{'key2'}{'key3'}
Run Code Online (Sandbox Code Playgroud)

作为Perl中常见的习惯用法,并且在其他语言(包括Python)中可能并且经常有用,那么除非您想使用n-1中间变量来n深度提取字典条目层,否则这似乎是一个不错的选择.重写类subsrefsubsasgn操作似乎很容易{}就像以前那样做同样的事情(),一切都应该有效.

当我尝试它时,它不会.

这是我的代码.(我把它减少到一个最小的情况.这里没有实现实际的字典,每个对象有一个键和一个值,但这足以证明问题.)

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
Run Code Online (Sandbox Code Playgroud)

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

t = TestBraces;
t{'foo'} = 'bar'
Run Code Online (Sandbox Code Playgroud)

(很明显,分配工作来自默认显示输出t.)所以subsasgn看起来工作正常.

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

t{'foo'}
??? Error using ==> subsref
Too many output arguments.
Run Code Online (Sandbox Code Playgroud)

错误消息对我来说没有任何意义,并且我的subsref处理程序的第一个可执行行的断点永远不会被命中,所以至少表面上这看起来像一个MATLAB问题,而不是我的代码中的错误.

显然,允许使用()括号下标的字符串参数,因为如果您更改代码而不是使用,则此方法可以正常工作.(除此之外,你不能嵌套下标操作,这是练习的目标.)(){}

无论是洞察我在我的代码中做错了什么,任何使我正在做的事情变得不可行的限制,或者嵌套字典的替代实现都会受到赞赏.

kwa*_*ord 10

简短的回答,将此方法添加到您的班级:

function n = numel(obj, varargin)
    n = 1;
end
Run Code Online (Sandbox Code Playgroud)

编辑:答案很长.

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

>> c = {1,2,3,4,5};
>> [a,b,c] = c{[1 3 5]}
a =
     1
b =
     3
c =
     5
Run Code Online (Sandbox Code Playgroud)

预期的输出数量subsref是根据索引数组的大小确定的.在这种情况下,索引数组的大小为3,因此有三个输出.

现在,再看一下:

t{'foo'}
Run Code Online (Sandbox Code Playgroud)

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

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

作为类设计者,您必须确保内置numericl函数返回的n值与该对象的类设计一致.如果n与重载的subsref函数的nargout或重载的subsasgn函数的nargin不同,则需要重载numel以返回与类的subsref和subsasgn函数一致的n值.否则,MATLAB在调用这些函数时会产生错误.

你有它.

  • 从R2015b开始,您应该重载`numArgumentsFromSubscript`函数 - 请参阅http://stackoverflow.com/questions/8713730/matlab-subsref-with-string-argument-fails-why和Mathworks页面https://www.mathworks的答案.com/help/matlab/matlab_oop/overloading-numel-subsref-and-subsasgn.html. (2认同)