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
层深,这似乎一个不错的选择。重写类的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问题,而不是我代码中的错误。
显然允许()
括号下标的字符串参数,因为如果您将代码更改为使用()
而不是{}
,则此方法可以正常工作。 (除此之外,你不能嵌套下标操作,这是练习的目标。)
要么深入了解我在代码中做错了什么,使我做的事情变得不可行的任何限制,或嵌套词典的替代实现都会受到赞赏。
答案 0 :(得分:9)
简短回答,将此方法添加到您的班级:
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个东西。所以,论点不匹配。幸运的是,我们可以通过改变MATLAB通过重载numel
来确定预期输出数量的方式来纠正问题。引用文档链接:
重要的是要注意到关于数字的重要性 重载subsref和subsasgn函数。在的情况下 用于括号和点索引的重载subsref函数(如上所述 在最后一段)中,numel用于计算数量 从subsref返回的预期输出(nargout)。对于超载 subsasgn函数,numel用于计算期望的数量 使用subsasgn分配的输入(nargin)。的值为 重载的subsasgn函数是由numel加2返回的值 (一个用于分配给变量,一个用于结构 下标数组。)
作为类设计者,您必须确保返回的值为n 内置的numel函数与类设计一致 那个对象。如果n不同于。的叙述 重载的subsref函数或重载的subsasgn的nargin 函数,那么你需要重载numel以返回n的值 与class的subsref和subsasgn函数一致。 否则,MATLAB在调用这些函数时会产生错误。
你有它。