Matlab subsref: {} со строковым аргументом не удается, почему? - PullRequest
5 голосов
/ 03 января 2012

В репозитории 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, а не ошибка в моем коде.

Явно строковые аргументы () в скобках разрешены , так как это работает нормально, если вы измените код для работы с () вместо {}. (За исключением того, что вы не можете вложить вложенные операции, которые являются целью упражнения.)

Буду признателен за понимание того, что я делаю неправильно в моем коде, за любые ограничения, которые делают невозможным то, что я делаю, или за альтернативные реализации вложенных словарей.

1 Ответ

9 голосов
/ 03 января 2012

Короткий ответ, добавьте этот метод в ваш класс:

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, и ваш подреф может выводить только одну вещь за раз.Итак, аргументы не совпадают.К счастью, мы можем исправить ситуацию, изменив способ, которым MATLAB определяет, сколько выходов ожидается, перегружая numel.Цитируется из ссылки на документ:

Важно отметить важность цифры в отношении перегруженных функций subsref и subsasgn.В случае перегруженной функции subsref для индексации фигурных скобок и точек (как описано в последнем абзаце), цифра используется для вычисления количества ожидаемых выходных данных (nargout), возвращаемых из subsref.Для перегруженной функции subsasgn цифра используется для вычисления количества ожидаемых входных данных (nargin), которые должны быть назначены с использованием subsasgn.Значение nargin для перегруженной функции subsasgn - это значение, возвращаемое цифрой плюс 2 (одно для присваиваемой переменной и одно для массива структуры индексов).

Как разработчик класса, вы должны убедиться, чтозначение n, возвращаемое встроенной функцией нумерации, согласуется с дизайном класса для этого объекта.Если n отличается от nargout для перегруженной функции subsref или nargin для перегруженной функции subsasgn, то вам необходимо перегрузить null, чтобы вернуть значение n, которое соответствует функциям класса subref и subsasgn.В противном случае MATLAB выдает ошибки при вызове этих функций.

И вот оно у вас.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...