MATLAB Language
Полезные трюки
Поиск…
Полезные функции, которые работают с ячейками и массивами
Этот простой пример дает объяснение некоторых функций, которые я нашел чрезвычайно полезными, так как я начал использовать MATLAB: cellfun
, arrayfun
. Идея состоит в том, чтобы взять переменную класса массива или ячейки, перебрать все ее элементы и применить выделенную функцию для каждого элемента. Приложенная функция может быть анонимной, что обычно является случаем, или любая регулярная функция определяется в файле * .m.
Начнем с простой проблемы и скажем, что нам нужно найти список файлов * .mat, заданных в этой папке. В этом примере сначала создадим некоторые файлы * .mat в текущей папке:
for n=1:10; save(sprintf('mymatfile%d.mat',n)); end
После выполнения кода должно быть 10 новых файлов с расширением * .mat. Если мы запустим команду для перечисления всех файлов * .mat, например:
mydir = dir('*.mat');
мы должны получить массив элементов структуры dir; MATLAB должен дать аналогичный результат:
10x1 struct array with fields:
name
date
bytes
isdir
datenum
Как вы можете видеть, каждый элемент этого массива представляет собой структуру с несколькими полями. Вся информация действительно важна для каждого файла, но в 99% меня больше интересуют имена файлов и ничего больше. Чтобы извлечь информацию из массива структуры, я использовал для создания локальной функции, которая включала бы создание временных переменных правильного размера, для циклов, извлечения имени из каждого элемента и сохранения его в созданной переменной. Более простой способ добиться точно такого же результата - использовать одну из вышеупомянутых функций:
mydirlist = arrayfun(@(x) x.name, dir('*.mat'), 'UniformOutput', false)
mydirlist =
'mymatfile1.mat'
'mymatfile10.mat'
'mymatfile2.mat'
'mymatfile3.mat'
'mymatfile4.mat'
'mymatfile5.mat'
'mymatfile6.mat'
'mymatfile7.mat'
'mymatfile8.mat'
'mymatfile9.mat'
Как эта функция работает? Обычно это два параметра: дескриптор функции как первый параметр и массив. Затем функция будет работать с каждым элементом данного массива. Третий и четвертый параметры являются необязательными, но важными. Если мы знаем, что выход не будет регулярным, он должен быть сохранен в ячейке. Это должно указывать на значение false
для UniformOutput
. По умолчанию эта функция пытается вернуть регулярный вывод, такой как вектор чисел. Например, давайте выберем информацию о том, сколько места на диске занимает каждый файл в байтах:
mydirbytes = arrayfun(@(x) x.bytes, dir('*.mat'))
mydirbytes =
34560
34560
34560
34560
34560
34560
34560
34560
34560
34560
или килобайт:
mydirbytes = arrayfun(@(x) x.bytes/1024, dir('*.mat'))
mydirbytes =
33.7500
33.7500
33.7500
33.7500
33.7500
33.7500
33.7500
33.7500
33.7500
33.7500
На этот раз выход является регулярным вектором double. UniformOutput
было установлено значение true
.
cellfun
- аналогичная функция. Разница между этой функцией и arrayfun
заключается в том, что cellfun
работает с переменными класса ячеек. Если мы хотим извлечь только имена с указанием списка имен файлов в ячейке «mydirlist», нам просто нужно будет запустить эту функцию следующим образом:
mydirnames = cellfun(@(x) x(1:end-4), mydirlist, 'UniformOutput', false)
mydirnames =
'mymatfile1'
'mymatfile10'
'mymatfile2'
'mymatfile3'
'mymatfile4'
'mymatfile5'
'mymatfile6'
'mymatfile7'
'mymatfile8'
'mymatfile9'
Опять же, поскольку выход не является регулярным вектором чисел, выход должен быть сохранен в переменной ячейки.
В приведенном ниже примере я объединяю две функции в одном и возвращаю только список имен файлов без расширения:
cellfun(@(x) x(1:end-4), arrayfun(@(x) x.name, dir('*.mat'), 'UniformOutput', false), 'UniformOutput', false)
ans =
'mymatfile1'
'mymatfile10'
'mymatfile2'
'mymatfile3'
'mymatfile4'
'mymatfile5'
'mymatfile6'
'mymatfile7'
'mymatfile8'
'mymatfile9'
Это безумие, но очень возможно, потому что arrayfun
возвращает ячейку, которая является ожидаемым вводом cellfun
; примечание к этому состоит в том, что мы можем заставить любую из этих функций вернуть результаты в переменную ячейки, установив UniformOutput
в false, явно. Мы всегда можем получать результаты в ячейке. Возможно, мы не сможем получить результаты в регулярном векторе.
Существует еще одна аналогичная функция, которая работает с полями: structfun
. Я не особо нашел его полезным, как два других, но в некоторых ситуациях он будет сиять. Если, например, хотелось бы знать, какие поля являются числовыми или нечисловыми, следующий код может дать ответ:
structfun(@(x) ischar(x), mydir(1))
Первое и второе поля структуры dir имеют тип char. Следовательно, выход:
1
1
0
0
0
Кроме того, вывод является логическим вектором true
/ false
. Следовательно, он является регулярным и может быть сохранен в векторе; нет необходимости использовать класс ячеек.
Настройки форматирования кода
Вы можете изменить предпочтение «Складка кода» в соответствии с вашими потребностями. Таким образом, сгибание кода можно установить enable / able для конкретных конструкций (например: if block
, for loop
, Sections
...).
Чтобы изменить параметры сгиба, перейдите в «Настройки» -> «Сгиб кода»:
Затем вы можете выбрать, какую часть кода можно сложить.
Некоторая информация:
- Обратите внимание, что вы также можете развернуть или свернуть весь код в файле, поместив курсор в любом месте файла, щелкнув правой кнопкой мыши, а затем выберите «Складка кода»> «Развернуть все» или «Сложить код»> «Сложить все» из контекстного меню.
- Обратите внимание, что сгибание является постоянным, в том смысле, что часть кода, который был расширен / свернут, сохранит свой статус после того, как Matlab или m-файл будут закрыты и снова открыты.
Пример. Чтобы включить фальцовку для разделов:
Интересным вариантом является возможность сброса разделов. Разделы разделены знаком двух процентов (
%%
).Пример. Чтобы включить его, установите флажок «Разделы»:
Затем вместо длинного исходного кода, похожего на:
Вы сможете сворачивать разделы, чтобы иметь общий обзор вашего кода:
Извлечь фигурные данные
В нескольких случаях у меня была интересная фигура, которую я сохранил, но я потерял доступ к ее данным. В этом примере показано, как получить информацию об извлечении из фигуры.
Ключевыми функциями являются findobj и get . findobj возвращает обработчик объекту, которому присвоены атрибуты или свойства объекта, такие как Type
или Color
и т. д. После того, как объект линии найден, get может вернуть любое значение, удерживаемое свойствами. Оказывается, объекты Line
сохраняют все данные в следующих свойствах: XData
, YData
и ZData
; последний обычно равен 0, если фигура не содержит 3D-график.
Следующий код создает примерный рисунок, который показывает две строки: функцию sin и порог и легенду
t = (0:1/10:1-1/10)';
y = sin(2*pi*t);
plot(t,y);
hold on;
plot([0 0.9],[0 0], 'k-');
hold off;
legend({'sin' 'threshold'});
Первое использование findobj возвращает два обработчика в обе строки:
findobj(gcf, 'Type', 'Line')
ans =
2x1 Line array:
Line (threshold)
Line (sin)
Чтобы сузить результат, findobj также может использовать комбинацию логических операторов -and
, -or
и имена свойств. Например, я могу найти объект линии, чье имя DiplayName
является sin
и читать его XData
и YData
.
lineh = findobj(gcf, 'Type', 'Line', '-and', 'DisplayName', 'sin');
xdata = get(lineh, 'XData');
ydata = get(lineh, 'YData');
и проверьте, равны ли данные.
isequal(t(:),xdata(:))
ans =
1
isequal(y(:),ydata(:))
ans =
1
Аналогично, я могу сузить результаты, исключив черную линию (порог):
lineh = findobj(gcf, 'Type', 'Line', '-not', 'Color', 'k');
xdata = get(lineh, 'XData');
ydata = get(lineh, 'YData');
и последняя проверка подтверждает, что данные, извлеченные из этого рисунка, совпадают:
isequal(t(:),xdata(:))
ans =
1
isequal(y(:),ydata(:))
ans =
1
Функциональное программирование с использованием анонимных функций
Анонимные функции могут использоваться для функционального программирования. Основная проблема, которую нужно решить, заключается в том, что для привязки рекурсии нет встроенного способа, но это может быть реализовано в одной строке:
if_ = @(bool, tf) tf{2-bool}();
Эта функция принимает логическое значение и массив ячеек из двух функций. Первая из этих функций оценивается, если логическое значение оценивается как true, а второе, если логическое значение оценивается как false. Теперь мы легко можем записать факториальную функцию:
fac = @(n,f) if_(n>1, {@()n*f(n-1,f), @()1});
Проблема здесь в том, что мы не можем напрямую вызывать рекурсивный вызов, так как функция еще не назначена переменной при оценке правой стороны. Однако мы можем завершить этот шаг, написав
factorial_ = @(n)fac(n,fac);
Теперь @(n)fac(n,fac)
эвакуирует факториальную функцию рекурсивно. Другой способ сделать это в функциональном программировании с помощью y-combinator, который также может быть легко реализован:
y_ = @(f)@(n)f(n,f);
С помощью этого инструмента факториальная функция еще короче:
factorial_ = y_(fac);
Или напрямую:
factorial_ = y_(@(n,f) if_(n>1, {@()n*f(n-1,f), @()1}));
Сохранить несколько фигур в том же файле .fig
Поместив несколько графических фигур в графический массив, несколько фигур можно сохранить в том же файле .fig
h(1) = figure;
scatter(rand(1,100),rand(1,100));
h(2) = figure;
scatter(rand(1,100),rand(1,100));
h(3) = figure;
scatter(rand(1,100),rand(1,100));
savefig(h,'ThreeRandomScatterplots.fig');
close(h);
Это создает 3 диаграммы рассеяния случайных данных, каждая часть графического массива h. Затем графический массив можно сохранить с помощью savefig, как с обычной фигурой, но с дескриптором графического массива в качестве дополнительного аргумента.
Интересное замечание состоит в том, что цифры будут оставаться так же, как и при их открытии.
Блоки комментариев
Если вы хотите прокомментировать часть своего кода, тогда комментарии могут быть полезными. Блок комментариев начинается с %{
в новой строке и заканчивается %}
в другой новой строке:
a = 10;
b = 3;
%{
c = a*b;
d = a-b;
%}
Это позволяет вам складывать разделы, которые вы прокомментировали, чтобы сделать код более чистым и компактным.
Эти блоки также полезны для включения / выключения частей вашего кода. Все, что вам нужно сделать, чтобы раскомментировать блок, - это добавить еще %
до того, как оно будет выглядеть:
a = 10;
b = 3;
%%{ <-- another % over here
c = a*b;
d = a-b;
%}
Иногда вы хотите прокомментировать раздел кода, но не затрагивая его отступ:
for k = 1:a
b = b*k;
c = c-b;
d = d*c;
disp(b)
end
Обычно, когда вы отмечаете блок кода и нажимаете Ctrl + r для его комментирования (тем самым добавляя %
автоматически ко всем строкам, тогда, когда вы нажимаете Ctrl + i для автоматического отступа, блок кода перемещается из его правильной иерархической место и переместился слишком сильно вправо:
for k = 1:a
b = b*k;
% c = c-b;
% d = d*c;
disp(b)
end
Способ решить это - использовать блоки комментариев, поэтому внутренняя часть блока остается правильно отступом:
for k = 1:a
b = b*k;
%{
c = c-b;
d = d*c;
%}
disp(b)
end