matlab subsref: {} 与字符串参数失败,为什么?
Mathworks 文件交换存储库中有一些哈希或字典类的实现。我所看到的所有内容都使用括号重载来进行关键引用,例如,
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
的第一个可执行行处有一个断点handler 从未被命中,所以至少表面上这看起来像是 MATLAB 问题,而不是我的代码中的错误。
显然,()
括号下标的字符串参数是允许的,因为如果您将代码更改为使用 ()
而不是 {}。 (除非你不能嵌套下标操作,这是练习的对象。)
要么深入了解我在代码中做错了什么,使我正在做的事情不可行的任何限制,要么嵌套字典的替代实现将不胜感激。
There are a few implementations of a hash or dictionary class in the Mathworks File Exchange repository. All that I have looked at use parentheses overloading for key referencing, e.g.
d = Dict;
d('foo') = 'bar';
y = d('foo');
which seems a reasonable interface. It would be preferable, though, if you want to easily have dictionaries which contain other dictionaries, to use braces {}
instead of parentheses, as this allows you to get around MATLAB's (arbitrary, it seems) syntax limitation that multiple parentheses are not allowed but multiple braces are allowed, i.e.
t{1}{2}{3} % is legal MATLAB
t(1)(2)(3) % is not legal MATLAB
So if you want to easily be able to nest dictionaries within dictionaries,
dict{'key1'}{'key2'}{'key3'}
as is a common idiom in Perl and is possible and frequently useful in other languages including Python, then unless you want to use n-1
intermediate variables to extract a dictionary entry n
layers deep, this seems a good choice. And it would seem easy to rewrite the class's subsref
and subsasgn
operations to do the same thing for {}
as they previously did for ()
, and everything should work.
Except it doesn't when I try it.
Here's my code. (I've reduced it to a minimal case. No actual dictionary is implemented here, each object has one key and one value, but this is enough to demonstrate the problem.)
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
Using this code, I can assign as expected:
t = TestBraces;
t{'foo'} = 'bar'
(And it is clear that the assignment work from the default display output for t
.) So subsasgn
appears to work correctly.
But I can't retrieve the value (subsref
doesn't work):
t{'foo'}
??? Error using ==> subsref
Too many output arguments.
The error message makes no sense to me, and a breakpoint at the first executable line of my subsref
handler is never hit, so at least superficially this looks like a MATLAB issue, not a bug in my code.
Clearly string arguments to ()
parenthesis subscripts are allowed, since this works fine if you change the code to work with ()
instead of {}
. (Except then you can't nest subscript operations, which is the object of the exercise.)
Either insight into what I'm doing wrong in my code, any limitations that make what I'm doing unfeasible, or alternative implementations of nested dictionaries would be appreciated.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
简短的答案,将此方法添加到您的类中:
编辑:长答案。
尽管 subsref 的函数签名出现在文档中,但它实际上是一个 varargout 函数 - 它可以生成可变数量的输出参数。大括号和点索引都可以产生多个输出,如下所示:
subsref
预期的输出数量是根据索引数组的大小确定的。在本例中,索引数组的大小为 3,因此有三个输出。现在,再看看:
索引数组的大小是多少?另外 3. MATLAB 并不关心您打算将其解释为字符串而不是数组。它只是看到输入的大小是 3 并且你的 subsref 一次只能输出 1 个东西。因此,论点不匹配。幸运的是,我们可以通过重载
numel 来更改 MATLAB 确定预期输出数量的方式来纠正问题
。引用自文档链接:现在你就得到了它。
Short answer, add this method to your class:
EDIT: The long answer.
Despite the way that
subsref
's function signature appears in the documentation, it's actually a varargout function - it can produce a variable number of output arguments. Both brace and dot indexing can produce multiple outputs, as shown here:The number of outputs expected from
subsref
is determined based on the size of the indexing array. In this case, the indexing array is size 3, so there's three outputs.Now, look again at:
What's the size of the indexing array? Also 3. MATLAB doesn't care that you intend to interpret this as a string instead of an array. It just sees that the input is size 3 and your subsref can only output 1 thing at a time. So, the arguments mismatch. Fortunately, we can correct things by changing the way that MATLAB determines how many outputs are expected by overloading
numel
. Quoted from the doc link:And there you have it.