Почему MATLAB выдает ошибку "слишком много выходных аргументов" при перегрузке subsref (подписанная ссылка)?

как игрушки, например, у меня есть класс, который просто создает вектор или матрицу в объекте и включает в себя метку, когда он был создан. Я пытаюсь перегрузить subsref, так что

  1. () ссылка работает точно так же, как и со стандартными типами векторов и матриц
  2. {} ссылка работает в ровно так же, как () ссылка (ничего общего с ячейками в других слова)
  3. . ссылка позволяет мне получить доступ к частная свойства объекта и других полей, которые технически не являются свойствами.

код:

classdef TimeStampValue

    properties (Access = private)
        time;
        values;
    end

    methods
        %% Constructor
        function x = TimeStampValue(values)
            x.time = now();
            x.values = values;
        end

        %% Subscripted reference
        function x = subsref(B, S)
            switch S.type
                case '()'
                    v = builtin('subsref', B.values, S);
                    x = TimeStampValue(v);
                case '{}'
                    S.type = '()';
                    v = builtin('subsref', B.values, S);
                    x = TimeStampValue(v);
                case '.'
                    switch S.subs
                        case 'time'
                            x = B.time;
                        case 'values'
                            x = B.values;
                        case 'datestr'
                            x = datestr(B.time);
                    end
            end
        end

        function disp(x)
            fprintf('t%dn', x.time)
            disp(x.values)
        end   

    end

end

однако brace {} ссылки не работают. Я запускаю этот код

clear all
x = TimeStampValue(magic(3));
x{1:2}

и я получаю эту ошибку:

Error using TimeStampValue/subsref
Too many output arguments.
Error in main (line 3)
x{1:2} 

MException.last дает мне эту информацию:

identifier: 'MATLAB:maxlhs'
   message: 'Too many output arguments.'
     cause: {0x1 cell}
     stack: [1x1 struct]

что не полезно, потому что единственное, что в стек исключений-это файл, содержащий три строки кода, которые я запускал выше.

я поставил точку останова в первой строке оператора switch в subsref но MATLAB никогда не достигает его.

в чем дело? Оба!--5--> и . ссылки на работу, как и следовало ожидать, так почему бы и нет {} ссылки на работу?

4 ответов


при перегрузке фигурных скобок {} чтобы вернуть другое количество выходных аргументов, чем обычно, также необходимо перегрузить numel для возврата предполагаемого числа (1, в данном случае). обновление: по состоянию на R2015b новая функция numArgumentsFromSubscript был создан для перегрузки вместо numel. Проблема остается той же, но эта функция должна быть перегружена вместо numel как я описываю в оригинале ответ ниже. Смотрите также страницу "изменить nargout и nargin для методов индексирования". Отрывок:

когда класс перегрузки numArgumentsFromSubscript, MATLAB вызывает этот метод вместо numel чтобы вычислить количество аргументов для subsref nargout и subsasgn nargin.

если классы не перегружайте numArgumentsFromSubscript, MATLAB вызывает numel для вычисления значений nargout или nargin.

другое объяснение ниже следует основная проблема (необходимо указать количество выходных аргументов).


оригинальный ответ (использовать numArgumentsFromSubscript вместо numel для R2015b+)

для обработки возможности списка выходных аргументов, разделенных запятыми, при индексировании фигурными скобками MATLAB вызывает numel определить количество выходных аргументов по размеру входных индексов (согласно это MathWorks ответ). Если количество выходных аргументов в определение перегруженного subsref не соответствует (т. е. меньше) номеру, указанному numel, вы получаете ошибку" слишком много выходных аргументов". Как указано в MathWorks:

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

С x{1:2} обычно имеет два выхода (X{1},X{2}), определением function x = subsref(B, S) несовместимо для этого входа. Решение включить в класс простой numel метод для перегрузки встроенной функции, следующим образом:

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

теперь {} индексирование работает по назначению, имитируя ():

>> clear all % needed to reset the class definition
>> x = TimeStampValue(magic(3));
>> x(1:2)
ans = 
    7.355996e+05
     8     3
>> x{1:2}
ans = 
    7.355996e+05
     8     3
перегрузка фигурных скобок таким образом видимо "определенный тип кода, который мы [MathWorks] не ожидали, что клиенты будут писать". Компании MathWorks рекомендует:

если вы разрабатываете свой класс для вывода только одного аргумента, не рекомендуется использовать индексирование фигурной скобки, которое требует перегрузки NUMEL. Вместо этого рекомендуется использовать индексирование smooth brace ().

обновление: интересно, что r2015b примечания к выпуску состояние:

перед выпуском MATLAB R2015b MATLAB неправильно рассчитал количество аргументов ожидается для выходов из subsref и материалы для subsasgn для некоторых выражений индексирования, возвращающих или назначающих список, разделенный запятыми.

С выпуском R2015b MATLAB правильно вычисляет значения nargout и nargin в соответствии с количеством аргументов, требуемых выражением индексирования.

так, может быть, это теперь исправлено?


альтернативное решение, которое приходит на ум-это изменить function x = subsref(B, S) to function varargout = subsref(B, S) и добавления varargout=cell(1,numel(B)); varargout{1} = x;. Как отметил Amro в комментариях, предварительное выделение ячейки необходимо, чтобы избежать ошибки о неназначенном аргументе.


я только что столкнулся с той же проблемой. Что еще хуже, так это то, что количество выходных аргументов принудительно равно what numel() возвращает не только фигурные скобки {}, но и для точечной . операции.

это означает, что если numel() переопределяется, чтобы вернуть обычный prod(size(obj)), становится невозможным получить доступ к любым свойствам базового объекта (например,x.time в приведенном выше примере), как subsref() затем ожидается возврат несколько выходов.

но если numel() просто возвращает 1 вместо этого, он не соответствует prod(size(obj)), который является то, что большинство кода работает с числовыми значениями и на основе reshape() ожидает. Фактически, справка редактора MATLAB сразу же предполагает ,что " NUMEL(x) обычно быстрее, чем PROD(SIZE(x))", что предполагает, что они эквивалентны, но, по-видимому, нет.

возможным решением было бы сделать numel() возвращение prod(size(obj)) и писать явные функции getter / setter для всех этих свойств, например,

x.get_time()

в примере выше. Кажется, это работает, потому что вызовы методов, по-видимому, разрешаются до subsref() вызывается. Но тогда, если одно из свойств является матрицей, оно больше не может быть непосредственно индексировано, потому что Matlab не понимает цепную индексацию, т. е. вместо записи

x.matrix(1,:)

нужно писать

m = x.get_matrix();
m(1,:)

что некрасиво говорить наименьший.

это начинает немного расстраивать. Я все еще надеюсь, что я просто упустил что-то очевидное, я не могу поверить, что это так должно работать.


это решение, кажется, работает в 2014b (но не совсем уверен, почему)

classdef TestClass < handle
    methods

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

        function varargout = subsref(input,S)
            varargout = builtin('subsref',input,S);
        end

        function out = twoOutputs(~)
            out = {}; out{1} = 2; out{2} = 3; 
        end
    end
end

затем через командное окно

>> testClass = TestClass();
>> [a,b] = testClass.twoOutouts()
a =

     2

b =

     3

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

в моем случае я хочу!--3--> чтобы вернуть вектор коэффициентов, если P - скалярный полином. Если P является полиномиальной матрицы, P.coef должен возвращать массив ячеек одинакового размера P, в котором ячейка {i,j} содержит вектор коэффициента многочлена P(i,j).

проблема появилась, когда P.coef использовался с матрицей. Мое желаемое поведение возвращает только один объект в качестве ответа, но Matlab ожидает, что функция вернет numel(P) объекты.

я нашел очень простое решение. При объявлении subsref, я использовал один обязательный выходной и varargout:

function [R,varargout] = subsref(P,S) 

тело функции определяет R по мере необходимости, согласно моему дизайну. И в самом конце из функции я добавил:

varargout(1:nargout-1) = cell(1,nargout-1);

просто вернуть пустые матрицы в качестве дополнительных выходов, которые хочет Matlab.

это не должно создавать проблем, если функция всегда вызывается с одним выходным аргументом, например, как в R = P.coef. Если функция вызывается без назначения, пользователь увидит numel(P)-1 пустые матрицы, которая на самом деле не имеет большого значения. В любом случае, пользователь будет предупрежден об этом в справке.