Поиск…


Полезные функции, которые работают с ячейками и массивами

Этот простой пример дает объяснение некоторых функций, которые я нашел чрезвычайно полезными, так как я начал использовать 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


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow