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'}

就像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

使用此代码,我可以按预期分配:
t = TestBraces;
t{'foo'} = 'bar'

(很明显,分配工作是通过t的默认显示输出完成的。)因此subsasgn似乎可以正常工作。

但是我无法检索该值(subsref不起作用):
t{'foo'}
??? Error using ==> subsref
Too many output arguments.

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

显然,允许使用()括号下标的字符串参数,因为如果您更改代码以使其适用于()而不是{},则此方法很好用。 (否则,您不能嵌套下标操作,这是练习的对象。)

深入了解我在代码中正在做错的事情,使我正在做的事情变得不可行的任何限制,或者嵌套字典的其他实现都将受到赞赏。

最佳答案

简短的答案,将此方法添加到您的类(class):

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

编辑:长答案。

尽管subsref的函数签名在文档中出现了,但实际上它是一个varargout函数-它可以产生可变数量的输出参数。大括号和点索引都可以产生多个输出,如下所示:
>> c = {1,2,3,4,5};
>> [a,b,c] = c{[1 3 5]}
a =
     1
b =
     3
c =
     5

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

现在,再次查看:
t{'foo'}

索引数组的大小是多少?同样,3. MATLAB不在乎您打算将其解释为字符串而不是数组。它只是看到输入的大小为3,而您的subsref一次只能输出1件事。因此,参数不匹配。幸运的是,我们可以通过重载 numel 来改变MATLAB确定期望输出多少的方式来纠正问题。引自doc链接:



在那里,您拥有了它。

10-08 11:51