Обновление одного поля в каждом элементе массива структур Matlab

Предположим, у меня есть массив структур arr, где каждый элемент имеет набор полей, в том числе одно с именем val. Я хотел бы увеличить поле val каждого элемента на некоторую постоянную величину, например:

for i = 1:length(arr)
    arr(i).val = arr(i).val + 3;
end

Это, очевидно, работает, но я чувствую, что должен быть способ сделать это всего одной строкой кода (и без цикла for). Лучшее, что я придумал, это две строки, для которых требуется временная переменная:

newVals = num2cell([arr.val] + 3);
[arr.val] = deal(newVals{:});

Есть идеи? Спасибо.


person Carl    schedule 15.02.2012    source источник


Ответы (4)


Просто примечание, deal там не нужно:

[arr.val] = newVals{:}; % achieves the same as deal(newVals{:})

Единственный другой известный мне способ сделать это (без цикла foor) — использовать arrayfun для перебора каждой структуры в массиве:

% make a struct array
arr = [ struct('val',0,'id',1), struct('val',0,'id',2), struct('val',0,'id',3) ]

% some attempts
[arr.val]=arr.val; % fine
[arr.val]=arr.val+3; % NOT fine :(

% works !
arr2 = arrayfun(@(s) setfield(s,'val',s.val+3),arr)

Эта последняя команда перебирает каждую структуру в arr и возвращает новую, где s.val было установлено на s.val=3.

Я думаю, что на самом деле это менее эффективно, чем ваш предыдущий двухстрочный цикл и цикл for, потому что он возвращает копию arr, а не работает на месте.

(Жаль, что Matlab не поддерживает многоуровневую индексацию, например [arr.val]=num2cell([arr.val]+3){:}).

person mathematical.coffee    schedule 16.02.2012
comment
Спасибо за совет по поводу deal. Я не знал о setfield, поэтому кажется, что это делается в одну строку, но, как вы говорите, это определенно хуже, чем решение для цикла for. Что касается такой индексации, я изучал ее некоторое время назад; по сути, Mathworks утверждает, что поддержка чего-либо подобного приведет к изменению синтаксического анализатора, нарушающему совместимость. Это позор, так как это беспокоит меня почти каждый раз, когда я пишу любой код Matlab. - person Carl; 18.02.2012
comment
Спасибо за этот ответ! В MATLAB 2013b [arr.val] = newVals{:} работает, НО arr.val = newVals{:}. Что именно делают скобки в этом случае? - person Berk U.; 14.02.2014
comment
arrayfun — это просто оболочка для цикла for в MATLAB, поэтому технически вы все еще используете циклы, хотя и замаскированные. - person Adriaan; 10.01.2016
comment
Одна из ситуаций, когда сделка полезна с массивом структур, — это когда вы хотите присвоить одно и то же значение полю каждого элемента массива структур. Например, если v = 9, вы можете написать [arr.val] = deal(v);, чтобы присвоить 9 полю val каждого элемента массива структур arr. - person Luca Citi; 05.05.2016
comment
[arr.val] = newVals{:}; работает, а [arr.val] = newVals(:); нет - это если newVals является вектором, а не массивом ячеек. Кто-нибудь может объяснить?! - person user2305193; 25.11.2017

Мне нравятся оригинальные идеи Карла и Math.coffee. У меня есть несколько похожих строк для выражения, поэтому для краткости моего основного кода я пошел дальше и сделал общую подфункцию

function varargout = clist(in)
varargout = {in{:}};
end

тогда я мог бы выразить каждую такую ​​строку достаточно читаемым образом

[arr.var]  = clist(num2cell([arr.var]+3));  
[arr.var2] = clist(num2cell([arr2.var]/5+33));  
person Stan Shepherd    schedule 27.10.2020

Все ли поля в этой структуре скалярные или одного размера? Если это так, идиоматический способ Matlab сделать это состоит в том, чтобы преобразовать вашу структуру в скалярную структуру с массивами в каждом из ее полей, а не в массив структур со скалярными значениями в полях. Затем вы можете выполнять векторизованные операции с полями, например arr.val = arr.val + 3;. Посмотрите, сможете ли вы изменить порядок данных. Такой способ гораздо более эффективен как по времени, так и по памяти; вероятно, поэтому Matlab не предоставляет удобного синтаксиса для работы с полями массивов структур.

person Andrew Janke    schedule 16.02.2012

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

x = (1:10)';
Y = rand(10,5);
l = plot(x,Y,'-k'); % returns an array of line handles in l
set(l,'Color','r'); % sets the property 'Color' for all the five lines in l
person Girardi    schedule 27.01.2017