Почему MATLAB выдает ошибку "слишком много выходных аргументов" при перегрузке subsref (подписанная ссылка)?
как игрушки, например, у меня есть класс, который просто создает вектор или матрицу в объекте и включает в себя метку, когда он был создан. Я пытаюсь перегрузить subsref
, так что
-
()
ссылка работает точно так же, как и со стандартными типами векторов и матриц -
{}
ссылка работает в ровно так же, как()
ссылка (ничего общего с ячейками в других слова) -
.
ссылка позволяет мне получить доступ к частная свойства объекта и других полей, которые технически не являются свойствами.
код:
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
пустые матрицы, которая на самом деле не имеет большого значения. В любом случае, пользователь будет предупрежден об этом в справке.