Как я могу индексировать массив 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

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

gnovice
источник
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.

Amro
источник
@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 нет указателей, с этим нужно жить.

Titus
источник
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.
нирвана-MSU
источник
На самом деле это не странно: MATLAB хранит список определенных функций, поиск не так уж и сложен. fevalделает «нормальную» вещь и, следовательно, может в полной мере использовать этот список. builtinнужно искать в другом месте, чтобы он нашел только встроенные функции. Вероятно, этот случай не оптимизирован почти так же хорошо, как «нормальный» случай, потому что зачем вкладывать деньги в оптимизацию чего-то, что используется не очень часто?
Крис Луенго