Как я могу индексировать массив MATLAB, возвращаемый функцией, без предварительного присвоения его локальной переменной?


363

Например, если я хочу прочитать среднее значение из magic(5), я могу сделать это так:

M = magic(5);
value = M(3,3);

чтобы получить value == 13. Я хотел бы иметь возможность сделать что-то вроде этого:

value = magic(5)(3,3);
value = (magic(5))(3,3);

обойтись без промежуточной переменной. Тем не менее, MATLAB жалуется Unbalanced or unexpected parenthesis or bracketна первую скобку перед 3.

Можно ли считать значения из массива / матрицы без предварительного присвоения переменной?


2
Я также нашел следующую статью на эту тему: mathworks.com/matlabcentral/newsreader/view_thread/280225 У кого-нибудь есть новая информация по этой теме, будет ли она реализована?

2
Этот синтаксис на самом деле отлично работает в Octave. Я обнаружил эту проблему, только когда у моих коллег, использующих MATLAB, были проблемы с запуском моего кода.
sffc

2
MATLAB в двух словах.
user76284

1
Рекурсивное извлечение также работает в Scilab ( scilab.org ) начиная с версии 6.
Стефан Моттлет

testmatrix('magi', 5)(3, 3)на Scilab и magic(5)(3, 3)на октаве работе , так как шарм!
Фоад

Ответы:


384

Это на самом деле это можно делать то , что вы хотите, но вы должны использовать функциональную форму оператора индексирования. Когда вы выполняете операцию индексирования с использованием (), вы фактически вызываете subsrefфункцию. Итак, даже если вы не можете сделать это:

value = magic(5)(3, 3);

Вы можете сделать это:

value = subsref(magic(5), struct('type', '()', 'subs', {{3, 3}}));

Уродливо, но возможно. ;)

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

subindex = @(A, r, c) A(r, c);     % An anonymous function for 2-D indexing
value = subindex(magic(5), 3, 3);  % Use the function to index the matrix

Однако, когда все сказано и сделано, решение для временных локальных переменных становится намного более читабельным, и определенно то, что я бы предложил.


26
Ну, что же вы знаете! хотя я согласен, что это довольно уродливо и, вероятно, менее читабельно, чем временное решение. +1 за внушительные знания о Matlab!
2

57
Это отвратительно, но очень четкий ответ. Хорошая работа! Должен был догадаться, что будет обратный путь. Я думаю, что я буду продолжать с переменной Temp.
Джо Кирни

29
Имейте в виду, что промежуточная переменная все еще полностью создана. Так что, если цель состоит в том, чтобы сохранить память, не создавая временную локальную переменную, не повезло.
Сэм Робертс

8
@SamRoberts: Вы не можете обойти это на языке строгой оценки, таком как Matlab. Основная причина, по которой люди хотят этого, - краткость / удобочитаемость, а не экономия памяти.
Механическая улитка

5
@SamRoberts: правда, но это действительно избавит вас от бремени вызова clearна временное (который никто никогда не делает) - временный , как правило , держаться дольше
Роди Oldenhuis

131

Пару дней назад была просто хорошая запись в блоге о Лорен о искусстве Matlab с парой драгоценных камней, которые могли бы помочь. В частности, используя вспомогательные функции, такие как:

paren = @(x, varargin) x(varargin{:});
curly = @(x, varargin) x{varargin{:}};

где paren()можно использовать как

paren(magic(5), 3, 3);

вернется

ans = 16

Я также предположил бы, что это будет быстрее, чем ответ gnovice, но я не проверял (Используйте профилировщик !!!). Тем не менее, вы также должны включить эти определения функций где-то. Я лично сделал их независимыми функциями на моем пути, потому что они очень полезны.

Эти и другие функции теперь доступны в дополнении « Functional Programming Constructs», которое доступно через обозреватель дополнений MATLAB или в File Exchange .


2
Это немного более общий вариант второй половины ответа gnovice; тоже хорошо.
Джо Кирни

Как насчет myfunc().attr?
gerrit

@gerrit, как на помощь? и поле x.attr () недоступно, если у вас нет набора инструментов базы данных.
Т. Фурфаро

@ Т.Фурфаро А? Если myfunc()возвращает структуру, которая включает в себя атрибут attr, то для доступа в attrданный момент мне нужно сделать S = myfunc(); S.attr. Вопрос в том, можем ли мы иметь вспомогательную функцию, как getattr(myfunc(), 'attr')в аналогии с parenи curlyпомощниками. Я не понимаю, как это связано с набором инструментов базы данных.
gerrit

2
@gerrit Извините, полная путаница (я не знал, что ваш "attr" был произвольным - в db tb определена такая степень поля). Я считаю, что вы ищете, getfield ()
Т. Фурфаро

75

Как вы относитесь к использованию недокументированных функций:

>> builtin('_paren', magic(5), 3, 3)               %# M(3,3)
ans =
    13

или для клеточных массивов:

>> builtin('_brace', num2cell(magic(5)), 3, 3)     %# C{3,3}
ans =
    13

Прямо как волшебство :)


ОБНОВИТЬ:

Плохая новость, вышеупомянутый взлом больше не работает в R2015b ! Это нормально, это была недокументированная функциональность, и мы не можем полагаться на нее как на поддерживаемую функцию :)

Для тех, кто интересуется, где найти этот тип вещей, посмотрите в папку fullfile(matlabroot,'bin','registry'). Там есть куча файлов XML, в которых перечислены все виды вкусностей. Имейте в виду, что прямой вызов некоторых из этих функций может легко привести к сбою сеанса MATLAB.


@RodyOldenhuis: Я не помню сейчас, наверное, я читал это в каком-то скрытом коде;)
Amro

2
Оператор двоеточия (:) должен использоваться с апострофами, ':'чтобы избежать ошибки Undefined function or variable "builtin".
Доминик

@Dominik: правильно, скажем, вы хотите нарезать 2-й столбец, который будет: builtin('_paren', magic(5), ':', 2)(в некоторых местах он работает без кавычек напрямую, :в отличие от ':', например, при запуске в командной строке напрямую, а не внутри функции. Я думаю, это ошибка в синтаксическом анализаторе!)
Amro

2
Я не думаю, что есть какой-то способ использовать endэто?
knedlsepp

2
@knedlsepp: Нет, к сожалению, весь этот endтрюк не работает в этом синтаксисе, вы должны быть явно указаны при индексации. (То же самое ограничение применяется для большинства других перечисленных ответов)
Amro

54

По крайней мере, в MATLAB 2013a вы можете использовать getfieldкак:

a=rand(5);
getfield(a,{1,2}) % etc

чтобы получить элемент в (1,2)


5
На самом деле это хороший метод. Есть ли недостатки?
mmumboss

6
@mmumboss: это недокументированное поведение, эта функция может исчезнуть без предварительного уведомления в будущих версиях. Помимо этого нет недостатков.
Даниэль

6
Начиная с MATLAB2017b, эта функциональность задокументирована.
njspeer

15

к сожалению, синтаксис вроде magic(5)(3,3)не поддерживается Matlab. вам нужно использовать временные промежуточные переменные. Вы можете освободить память после использования, например

tmp = magic(3);
myVar = tmp(3,3);
clear tmp

12

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

subs=@(M,i,j) M(i,j);
>> for nit=1:10;tic;subs(magic(100),1:10,1:10);tlap(nit)=toc;end;mean(tlap)

ans =

0.0103

>> for nit=1:10,tic;M=magic(100); M(1:10,1:10);tlap(nit)=toc;end;mean(tlap)

ans =

0.0101

На мой взгляд, суть в том, что в MATLAB нет указателей, с этим нужно жить.


6

Это может быть проще, если вы сделаете новую функцию:

function [ element ] = getElem( matrix, index1, index2 )
    element = matrix(index1, index2);
end

и затем используйте это:

value = getElem(magic(5), 3, 3);

1
но это именно то, что subrefделает ... но в более общем смысле.
Шай

2
да, более общий путь, но не дружелюбный ... на мой взгляд, очень уродливый.
Вугар

4

Ваша начальная запись является наиболее кратким способом сделать это:

M = magic(5);  %create
value = M(3,3);  % extract useful data
clear M;  %free memory

Если вы делаете это в цикле, вы можете просто каждый раз переназначать M и игнорировать оператор clear.


6
Я согласен, что это более кратко, и, как вы говорите, очистка - хорошая идея в цикле, но вопрос был именно в том, можно ли избежать промежуточного назначения.
Джо Кирни

1

Чтобы дополнить ответ Амро, вы можете использовать fevalвместо builtin. На самом деле нет никакой разницы, если только вы не попытаетесь перегрузить функцию оператора:

BUILTIN (...) аналогичен FEVAL (...), за исключением того, что он будет вызывать оригинальную встроенную версию функции, даже если существует перегруженная версия (чтобы это работало, вы никогда не должны перегружать BUILTIN).

>> feval('_paren', magic(5), 3, 3)               % M(3,3)
ans =
    13

>> feval('_brace', num2cell(magic(5)), 3, 3)     % C{3,3}
ans =
    13

Интересно то, что это fevalкажется чуть-чуть быстрее builtin(на ~ 3,5%), по крайней мере, в Matlab 2013b, что странно, учитывая, что fevalнужно проверить, не перегружена ли функция, в отличие от builtin:

>> tic; for i=1:1e6, feval('_paren', magic(5), 3, 3); end; toc;
Elapsed time is 49.904117 seconds.
>> tic; for i=1:1e6, builtin('_paren', magic(5), 3, 3); end; toc;
Elapsed time is 51.485339 seconds.

На самом деле это не странно: MATLAB хранит список определенных функций, поиск не так уж и сложен. fevalделает «нормальную» вещь и, следовательно, может в полной мере использовать этот список. builtinнужно искать в другом месте, чтобы он нашел только встроенные функции. Вероятно, этот случай не оптимизирован почти так же хорошо, как «нормальный» случай, потому что зачем вкладывать деньги в оптимизацию чего-то, что используется не очень часто?
Крис Луенго
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.