Функция Matlab обрабатывать рабочее пространство shenanigans - PullRequest
11 голосов
/ 29 декабря 2011

Короче говоря : есть ли элегантный способ ограничить область действия анонимных функций или Matlab не работает в этом примере?

У меня есть функция, которая создает дескриптор функции для использования в решателе трубопроводной сети. Он принимает в качестве входных данных состояние сети, которое включает в себя информацию о каналах и их соединениях (или ребрах и вершинах, если необходимо), создает большую строку, которая будет возвращать большую матрицу в форме функции, и «изгоняет» эту строку для создания дескриптора .

function [Jv,...] = getPipeEquations(Network)
... %// some stuff happens here

Jv_str = ['[listConnected(~endNodes,:)',...
    ' .* areaPipes(~endNodes,:);\n',...
    anotherLongString,']'];

Jv_str = sprintf(Jv_str); %// This makes debugging the string easier

eval(['Jv = @(v,f,rho)', Jv_str, ';']);

Эта функция работает как задумано, но всякий раз, когда мне нужно сохранить более поздние структуры данных, содержащие этот дескриптор функции, ей требуется нелепый объем памяти (150 МБ) - по совпадению примерно столько же, сколько все рабочее пространство Matlab на момент создания этой функции (~ 150 МБ). Переменные, которые требуются для этого дескриптора функции из рабочего пространства getPipeEquations, не слишком велики, но еще более странно то, что, когда я проверяю дескриптор функции:

>> f = functions(Network.jacobianFun)
f = 

     function: [1x8323 char]
         type: 'anonymous'
         file: '...\pkg\+adv\+pipe\getPipeEquations.m'
    workspace: {2x1 cell}

... поле рабочего пространства содержит все, что было у getPipeEquations (что, кстати, не всего рабочего пространства Matlab).

Если вместо этого я переместу оператор eval в подфункцию в попытке форсировать область действия, дескриптор сохранит гораздо более компактно (~ 1 МБ):

function Jv = getJacobianHandle(Jv_str,listConnected,areaPipes,endNodes,D,L,g,dz)
eval(['Jv = @(v,f,rho)', Jv_str, ';']);

Это ожидаемое поведение? Есть ли более элегантный способ ограничить область действия этой анонимной функции?

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

Ответы [ 3 ]

7 голосов
/ 30 декабря 2011

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

Вот минимальное воспроизведение.

function fcn = so_many_variables()
a = 1;
b = 2;
c = 3;
fcn = @(x) a+x;
a = 42;

И действительно, он захватывает копию всего окружающего рабочего пространства.

>> f = so_many_variables;
>> f_info = functions(f);
>> f_info.workspace{1}
ans = 
    a: 1
>> f_info.workspace{2}
ans = 
    fcn: @(x)a+x
      a: 1
      b: 2
      c: 3

Сначала это было для меня неожиданностью. Но это имеет смысл, когда вы думаете об этом: из-за присутствия feval и eval, Matlab не может на самом деле знать во время конструирования, на какие переменные анонимная функция на самом деле будет ссылаться. Таким образом, он должен захватывать все в области видимости на случай, если на них будут ссылаться динамически, как в этом надуманном примере. При этом используется значение foo, но Matlab не узнает об этом, пока вы не вызовете возвращенный дескриптор функции.

function fcn = so_many_variables()
a = 1;
b = 2;
foo = 42;
fcn = @(x) x + eval(['f' 'oo']);

Обходной путь, который вы делаете - изоляция конструкции функции в отдельной функции с минимальным рабочим пространством - звучит как правильное решение.

Вот обобщенный способ заставить ограниченное рабочее пространство встроить вашу анонимную функцию.

function eval_with_vars_out = eval_with_vars(eval_with_vars_expr, varargin)

% Assign variables to the local workspace so they can be captured
ewvo__reserved_names = {'varargin','eval_with_vars_out','eval_with_vars_expr','ewvo__reserved_names','ewvo_i'};
for ewvo_i = 2:nargin
    if ismember(inputname(ewvo_i), ewvo__reserved_names)
        error('variable name collision: %s', inputname(ewvo_i));
    end
    eval([ inputname(ewvo_i) ' = varargin{ewvo_i-1};']);
end
clear ewvo_i ewvo__reserved_names varargin;

% And eval the expression in that context
eval_with_vars_out = eval(eval_with_vars_expr);

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

Вы просто вызываете eval_with_vars () вместо eval () и передаете все входные переменные в качестве дополнительных аргументов. Тогда вам не нужно вводить определение статической функции для каждого из ваших создателей анонимных функций. Это будет работать до тех пор, пока вы заранее знаете, на какие переменные действительно будут ссылаться, что является тем же ограничением, что и подход с getJacobianHandle * 1025

Jv = eval_with_vars_out(['@(v,f,rho) ' Jv_str],listConnected,areaPipes,endNodes,D,L,g,dz);
2 голосов
/ 29 декабря 2011

Анонимные функции захватывают все в пределах своей области и сохраняют их в рабочем пространстве функции.См. Документацию MATLAB для анонимных функций

В частности:

"Переменные, указанные в теле выражения. MATLAB захватывает эти переменные и сохраняет их постоянными в течение всего времени жизнидескриптор функции.

Последним переменным должно быть присвоено значение в то время, когда вы создаете анонимную функцию, которая их использует. При построении MATLAB записывает текущее значение для каждой переменной, указанной в теле этой функции.Функция продолжит связывать это значение с переменной, даже если значение должно измениться в рабочей области или выйти из области видимости. "

0 голосов
/ 30 декабря 2011

Альтернативный способ решения вашей проблемы - использовать тот факт, что функция matlab save может использоваться для сохранения только тех переменных, которые вам нужны.У меня были проблемы с функцией save, позволяющей экономить слишком много данных (очень отличающийся от вашего контекста), но некоторые юридические соглашения об именах и использование подстановочных знаков в списке переменных устранили все мои проблемы.

...