MATLAB Language
Przydatne sztuczki
Szukaj…
Przydatne funkcje działające na komórkach i tablicach
Ten prosty przykład zawiera wyjaśnienie niektórych funkcji, które uważam za niezwykle przydatne, odkąd zacząłem używać MATLAB: cellfun
, arrayfun
. Chodzi o to, aby wziąć zmienną klasy tablicowej lub komórkowej, przejrzeć wszystkie jej elementy i zastosować dedykowaną funkcję dla każdego elementu. Zastosowana funkcja może być anonimowa, co zwykle jest przypadkiem, lub dowolną funkcją zwykłą zdefiniowaną w pliku * .m.
Zacznijmy od prostego problemu i powiedzmy, że musimy znaleźć listę plików * .mat dla danego folderu. W tym przykładzie najpierw utwórzmy niektóre pliki * .mat w bieżącym folderze:
for n=1:10; save(sprintf('mymatfile%d.mat',n)); end
Po wykonaniu kodu powinno być 10 nowych plików z rozszerzeniem * .mat. Jeśli uruchomimy polecenie, aby wyświetlić listę wszystkich plików * .mat, takich jak:
mydir = dir('*.mat');
powinniśmy uzyskać tablicę elementów struktury dir; MATLAB powinien dać wynik podobny do tego:
10x1 struct array with fields:
name
date
bytes
isdir
datenum
Jak widać, każdy element tej tablicy jest strukturą z kilkoma polami. Wszystkie informacje są rzeczywiście ważne w odniesieniu do każdego pliku, ale w 99% jestem raczej zainteresowany nazwami plików i niczym więcej. Aby wyodrębnić informacje z tablicy struktur, użyłem funkcji lokalnej, która wymagałaby utworzenia zmiennych czasowych o prawidłowym rozmiarze dla pętli, wyodrębnienia nazwy z każdego elementu i zapisania jej w utworzonej zmiennej. Znacznie łatwiejszym sposobem osiągnięcia dokładnie tego samego wyniku jest użycie jednej z wyżej wymienionych funkcji:
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'
Jak działa ta funkcja? Zwykle wymaga dwóch parametrów: uchwytu funkcji jako pierwszego parametru i tablicy. Funkcja będzie wtedy działać na każdym elemencie danej tablicy. Trzeci i czwarty parametr są opcjonalne, ale ważne. Jeśli wiemy, że dane wyjściowe nie będą regularne, należy je zapisać w komórce. Należy to zaznaczyć, ustawiając wartość false
na UniformOutput
. Domyślnie ta funkcja próbuje zwrócić regularne wyjście, takie jak wektor liczb. Na przykład wyodrębnijmy informacje o tym, ile miejsca na dysku zajmuje każdy plik w bajtach:
mydirbytes = arrayfun(@(x) x.bytes, dir('*.mat'))
mydirbytes =
34560
34560
34560
34560
34560
34560
34560
34560
34560
34560
lub kilobajty:
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
Tym razem wyjście jest regularnym wektorem podwójnym. UniformOutput
był ustawiony na true
.
cellfun
jest podobną funkcją. Różnica między tą funkcją a arrayfun
polega na tym, że cellfun
działa na zmiennych klasy komórek. Jeśli chcemy wyodrębnić tylko nazwy z listą nazw plików w komórce „mydirlist”, wystarczy uruchomić tę funkcję w następujący sposób:
mydirnames = cellfun(@(x) x(1:end-4), mydirlist, 'UniformOutput', false)
mydirnames =
'mymatfile1'
'mymatfile10'
'mymatfile2'
'mymatfile3'
'mymatfile4'
'mymatfile5'
'mymatfile6'
'mymatfile7'
'mymatfile8'
'mymatfile9'
Ponownie, ponieważ dane wyjściowe nie są regularnym wektorem liczb, dane wyjściowe należy zapisać w zmiennej komórkowej.
W poniższym przykładzie łączę dwie funkcje w jedną i zwracam tylko listę nazw plików bez rozszerzenia:
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'
Jest to szalone, ale bardzo możliwe, ponieważ arrayfun
zwraca komórkę, która ma cellfun
wprowadzona przez cellfun
; uwaga dodatkowa polega na tym, że możemy zmusić dowolną z tych funkcji do zwracania wyników w zmiennej komórkowej poprzez UniformOutput
ustawienie UniformOutput
na false. Zawsze możemy uzyskać wyniki w komórce. Możemy nie być w stanie uzyskać wyników w zwykłym wektorze.
Jest jeszcze jedna podobna funkcja, która działa na polach struktury: structfun
. Nie uważałem go za szczególnie przydatny jak pozostałe dwa, ale w niektórych sytuacjach świeciłby. Jeśli na przykład chcesz wiedzieć, które pola są numeryczne lub nienumeryczne, poniższy kod może dać odpowiedź:
structfun(@(x) ischar(x), mydir(1))
Pierwsze i drugie pole struktury dir jest typu char. Dlatego wynik jest następujący:
1
1
0
0
0
Ponadto dane wyjściowe są logicznym wektorem wartości true
/ false
. W związku z tym jest regularny i można go zapisać w wektorze; nie trzeba używać klasy komórki.
Preferencje składania kodu
Istnieje możliwość zmiany preferencji składania kodu w zależności od potrzeb. W ten sposób zwijanie kodu można ustawić włączanie / wyłączanie dla określonych konstrukcji (np. if block
, for loop
, Sections
...).
Aby zmienić preferencje składania, przejdź do Preferencje -> Składanie kodu:
Następnie możesz wybrać, która część kodu może zostać złożona.
Trochę informacji:
- Zauważ, że możesz również rozwinąć lub zwinąć cały kod w pliku, umieszczając kursor w dowolnym miejscu pliku, kliknij prawym przyciskiem myszy, a następnie wybierz polecenie Składanie kodu> Rozwiń wszystko lub Składanie kodu> Składaj wszystko z menu kontekstowego.
- Zauważ, że składanie jest trwałe, w tym sensie, że część kodu, który został rozwinięty / zwinięty, zachowa swój status po zamknięciu Matlaba lub pliku m i ponownym otwarciu.
Przykład: Aby włączyć składanie dla sekcji:
Ciekawą opcją jest umożliwienie składania sekcji. Sekcje są rozdzielone dwoma znakami procentowymi (
%%
).Przykład: Aby to włączyć, zaznacz pole „Sekcje”:
Następnie zamiast widzieć długi kod źródłowy podobny do:
Będziesz mógł składać sekcje, aby uzyskać ogólny przegląd swojego kodu:
Wyodrębnij dane liczbowe
Kilkakrotnie miałem ciekawą postać, którą zapisałem, ale straciłem dostęp do jej danych. Ten przykład pokazuje sztuczkę, jak uzyskać ekstrakt informacji z postaci.
Kluczowe funkcje to findobj i get . findobj zwraca moduł obsługi do obiektu o określonych atrybutach lub właściwościach obiektu, takich jak Type
lub Color
itp. Po znalezieniu obiektu liniowego get może zwrócić dowolną wartość przechowywaną przez właściwości. Okazuje się, że obiekty Line
przechowują wszystkie dane w następujących właściwościach: XData
, YData
i ZData
; ostatnia wynosi zwykle 0, chyba że figurka zawiera wykres 3D.
Poniższy kod tworzy przykładową ilustrację, która pokazuje dwie linie funkcję sin oraz próg i legendę
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'});
Pierwsze użycie findobj zwraca dwie procedury obsługi do obu linii:
findobj(gcf, 'Type', 'Line')
ans =
2x1 Line array:
Line (threshold)
Line (sin)
Aby zawęzić wynik, findobj może również używać kombinacji operatorów logicznych -and
, -or
i nazw właściwości. Na przykład mogę znaleźć obiekt linii, którego DiplayName
to sin
i odczytać jego XData
i YData
.
lineh = findobj(gcf, 'Type', 'Line', '-and', 'DisplayName', 'sin');
xdata = get(lineh, 'XData');
ydata = get(lineh, 'YData');
i sprawdź, czy dane są równe.
isequal(t(:),xdata(:))
ans =
1
isequal(y(:),ydata(:))
ans =
1
Podobnie mogę zawęzić wyniki, wykluczając czarną linię (próg):
lineh = findobj(gcf, 'Type', 'Line', '-not', 'Color', 'k');
xdata = get(lineh, 'XData');
ydata = get(lineh, 'YData');
a ostatnia kontrola potwierdza, że dane wyodrębnione z tej liczby są takie same:
isequal(t(:),xdata(:))
ans =
1
isequal(y(:),ydata(:))
ans =
1
Programowanie funkcjonalne za pomocą funkcji anonimowych
Do programowania funkcjonalnego można użyć funkcji anonimowych. Głównym problemem do rozwiązania jest to, że nie ma natywnego sposobu zakotwiczenia rekurencji, ale nadal można to zaimplementować w jednym wierszu:
if_ = @(bool, tf) tf{2-bool}();
Ta funkcja przyjmuje wartość logiczną i tablicę komórkową dwóch funkcji. Pierwsza z tych funkcji jest oceniana, jeśli wartość boolowska ma wartość true, a druga, jeśli wartość boolowska ma wartość false. Możemy teraz łatwo napisać funkcję silni:
fac = @(n,f) if_(n>1, {@()n*f(n-1,f), @()1});
Problem polega na tym, że nie możemy bezpośrednio wywołać wywołania rekurencyjnego, ponieważ funkcja nie jest jeszcze przypisana do zmiennej, gdy oceniana jest prawa strona. Możemy jednak wykonać ten krok pisząc
factorial_ = @(n)fac(n,fac);
Teraz @(n)fac(n,fac)
ewidencjonuje rekurencyjnie funkcję silni. Kolejny sposób na wykonanie tego w programowaniu funkcjonalnym za pomocą kombinatora Y, który można również łatwo wdrożyć:
y_ = @(f)@(n)f(n,f);
Dzięki temu narzędziu funkcja silnia jest jeszcze krótsza:
factorial_ = y_(fac);
Lub bezpośrednio:
factorial_ = y_(@(n,f) if_(n>1, {@()n*f(n-1,f), @()1}));
Zapisz wiele cyfr w tym samym pliku .fig
Umieszczając wiele uchwytów figur w tablicy graficznej, wiele figur można zapisać w tym samym pliku .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);
Tworzy to 3 wykresy rozrzutu losowych danych, każda część tablicy graficznej h. Następnie tablicę graficzną można zapisać za pomocą savefig jak na normalnej figurze, ale z uchwytem do tablicy graficznej jako dodatkowym argumentem.
Interesująca uwaga dodatkowa polega na tym, że figurki mają tendencję do pozostawania w takiej samej formie, w jakiej zostały zapisane podczas ich otwierania.
Bloki komentarzy
Jeśli chcesz skomentować część kodu, przydatne mogą być bloki komentarzy. Blok komentarza zaczyna się od %{
w nowym wierszu, a kończy na %}
w innym nowym wierszu:
a = 10;
b = 3;
%{
c = a*b;
d = a-b;
%}
Umożliwia to złożenie skomentowanych sekcji, aby kod był bardziej przejrzysty i zwarty.
Te bloki są również przydatne do włączania / wyłączania części kodu. Wszystko, co musisz zrobić, aby anulować komentarz do bloku, to dodać kolejny %
zanim zacznie obowiązywać:
a = 10;
b = 3;
%%{ <-- another % over here
c = a*b;
d = a-b;
%}
Czasami chcesz skomentować część kodu, ale bez wpływu na jego wcięcie:
for k = 1:a
b = b*k;
c = c-b;
d = d*c;
disp(b)
end
Zwykle po zaznaczeniu bloku kodu i naciśnięciu Ctrl + r, aby go skomentować (dodając %
automatycznie do wszystkich wierszy, a następnie po naciśnięciu Ctrl + i w celu automatycznego wcięcia, blok kodu przesuwa się z prawidłowej hierarchii miejsce i przesunął się za bardzo w prawo:
for k = 1:a
b = b*k;
% c = c-b;
% d = d*c;
disp(b)
end
Sposobem na rozwiązanie tego jest użycie bloków komentarzy, więc wewnętrzna część bloku pozostaje poprawnie wcięta:
for k = 1:a
b = b*k;
%{
c = c-b;
d = d*c;
%}
disp(b)
end