MATLAB Language
Trucchi utili
Ricerca…
Funzioni utili che operano su celle e array
Questo semplice esempio fornisce una spiegazione su alcune funzioni che ho trovato estremamente utili da quando ho iniziato ad usare MATLAB: cellfun
, arrayfun
. L'idea è di prendere una variabile di classe array o cellula, scorrere tutti i suoi elementi e applicare una funzione dedicata su ciascun elemento. Una funzione applicata può essere anonima, che di solito è un caso, o qualsiasi funzione regolare definita in un file * .m.
Iniziamo con un semplice problema e diciamo che dobbiamo trovare un elenco di file * .mat dati la cartella. Per questo esempio, per prima cosa creiamo alcuni file * .mat in una cartella corrente:
for n=1:10; save(sprintf('mymatfile%d.mat',n)); end
Dopo aver eseguito il codice, dovrebbero esserci 10 nuovi file con estensione * .mat. Se eseguiamo un comando per elencare tutti i file * .mat, ad esempio:
mydir = dir('*.mat');
dovremmo ottenere una serie di elementi di una struttura dir; MATLAB dovrebbe dare un risultato simile a questo:
10x1 struct array with fields:
name
date
bytes
isdir
datenum
Come puoi vedere, ogni elemento di questo array è una struttura con un paio di campi. Tutte le informazioni sono davvero importanti per ogni file, ma nel 99% sono piuttosto interessato ai nomi di file e nient'altro. Per estrarre informazioni da un array di strutture, ho usato per creare una funzione locale che implicherebbe la creazione di variabili temporali di una dimensione corretta, per cicli, estrarre un nome da ciascun elemento e salvarlo nella variabile creata. Un modo molto più semplice per ottenere esattamente lo stesso risultato è usare una delle funzioni di cui sopra:
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'
Come funziona questa funzione? Di solito prende due parametri: una funzione gestisce come primo parametro e un array. Una funzione quindi opererà su ciascun elemento di un dato array. Il terzo e il quarto parametro sono facoltativi ma importanti. Se sappiamo che un output non sarà regolare, deve essere salvato nella cella. Questo deve essere indicato impostando false
su UniformOutput
. Di default questa funzione tenta di restituire un output regolare come un vettore di numeri. Ad esempio, estrapiamo informazioni sulla quantità di spazio su disco utilizzato da ciascun file in byte:
mydirbytes = arrayfun(@(x) x.bytes, dir('*.mat'))
mydirbytes =
34560
34560
34560
34560
34560
34560
34560
34560
34560
34560
o kilobyte:
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
Questa volta l'output è un normale vettore di doppio. UniformOutput
stato impostato su true
per impostazione predefinita.
cellfun
è una funzione simile. La differenza tra questa funzione e arrayfun
è che cellfun
opera sulle variabili di classe delle celle. Se desideriamo estrarre solo i nomi dati da un elenco di nomi di file in una cella "mydirlist", avremmo solo bisogno di eseguire questa funzione come segue:
mydirnames = cellfun(@(x) x(1:end-4), mydirlist, 'UniformOutput', false)
mydirnames =
'mymatfile1'
'mymatfile10'
'mymatfile2'
'mymatfile3'
'mymatfile4'
'mymatfile5'
'mymatfile6'
'mymatfile7'
'mymatfile8'
'mymatfile9'
Di nuovo, poiché un output non è un normale vettore di numeri, un output deve essere salvato in una variabile di cella.
Nell'esempio seguente, combino due funzioni in una e restituisco solo un elenco di nomi di file senza un'estensione:
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'
È pazzesco ma molto possibile perché arrayfun
restituisce una cella che è l'input previsto di cellfun
; una nota a UniformOutput
è che possiamo forzare una di queste funzioni a restituire risultati in una variabile di cella impostando UniformOutput
su false, esplicitamente. Possiamo sempre ottenere risultati in una cella. Potremmo non essere in grado di ottenere risultati in un vettore normale.
C'è un'altra funzione simile che opera sui campi di una struttura: structfun
. Non l'ho trovato particolarmente utile come gli altri due, ma in alcune situazioni potrebbe brillare. Se ad esempio si desidera sapere quali campi sono numerici o non numerici, il seguente codice può dare la risposta:
structfun(@(x) ischar(x), mydir(1))
Il primo e il secondo campo di una struttura dir sono di tipo char. Pertanto, l'output è:
1
1
0
0
0
Inoltre, l'output è un vettore logico di true
/ false
. Di conseguenza, è regolare e può essere salvato in un vettore; non c'è bisogno di usare una classe di cellule.
Preferenze di piegatura del codice
È possibile modificare la preferenza di piegatura del codice in base alle proprie esigenze. In questo modo è possibile impostare l'abilitazione / disabilitazione del codice per specifici costrutti (es: if block
, for loop
, Sections
...).
Per modificare le preferenze di piegatura, vai a Preferenze -> Piegatura codice:
Quindi puoi scegliere quale parte del codice può essere piegata.
Alcune informazioni:
- Tieni presente che puoi anche espandere o comprimere tutto il codice in un file posizionando il cursore ovunque all'interno del file, fare clic con il pulsante destro del mouse e quindi selezionare Piegatura codice> Espandi tutto o Piegatura codice> Piega tutto dal menu di scelta rapida.
- Si noti che il folding è persistente, nel senso che parte del codice che è stato espanso / compresso manterrà il proprio stato dopo Matlab o il file m è stato chiuso e riaperto.
Esempio: per abilitare il piegamento per le sezioni:
Un'opzione interessante è abilitare a piegare le sezioni. Le sezioni sono delimitate da due segni di percentuale (
%%
).Esempio: per abilitarlo seleziona la casella "Sezioni":
Quindi, invece di vedere un codice sorgente lungo simile a:
Potrai piegare le sezioni per avere una panoramica generale del tuo codice:
Estrai dati figure
In alcune occasioni, ho avuto una figura interessante che ho salvato, ma ho perso un accesso ai suoi dati. Questo esempio mostra un trucco su come ottenere informazioni da una figura.
Le funzioni chiave sono findobj e get . findobj restituisce un gestore a un oggetto dati attributi o proprietà dell'oggetto, come Type
o Color
, ecc. Una volta che un oggetto linea è stato trovato, get può restituire qualsiasi valore posseduto dalle proprietà. Si scopre che gli oggetti Line
contengono tutti i dati nelle seguenti proprietà: XData
, YData
e ZData
; l'ultimo è di solito 0 a meno che una figura contenga un grafico 3D.
Il codice seguente crea una figura di esempio che mostra due linee una funzione sin e una soglia e una legenda
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'});
Il primo utilizzo di findobj restituisce due gestori su entrambe le righe:
findobj(gcf, 'Type', 'Line')
ans =
2x1 Line array:
Line (threshold)
Line (sin)
Per restringere il risultato, findobj può anche utilizzare la combinazione di operatori logici -and
, -or
nomi di proprietà. Ad esempio, posso trovare un oggetto linea il cui DiplayName
è sin
e leggere i suoi XData
e YData
.
lineh = findobj(gcf, 'Type', 'Line', '-and', 'DisplayName', 'sin');
xdata = get(lineh, 'XData');
ydata = get(lineh, 'YData');
e controllare se i dati sono uguali.
isequal(t(:),xdata(:))
ans =
1
isequal(y(:),ydata(:))
ans =
1
Allo stesso modo, posso restringere i miei risultati escludendo la linea nera (soglia):
lineh = findobj(gcf, 'Type', 'Line', '-not', 'Color', 'k');
xdata = get(lineh, 'XData');
ydata = get(lineh, 'YData');
e l'ultimo controllo conferma che i dati estratti da questa figura sono gli stessi:
isequal(t(:),xdata(:))
ans =
1
isequal(y(:),ydata(:))
ans =
1
Programmazione funzionale usando le funzioni anonime
Le funzioni anonime possono essere utilizzate per la programmazione funzionale. Il problema principale da risolvere è che non esiste un modo nativo per l'ancoraggio di una ricorsione, ma questo può ancora essere implementato in una singola riga:
if_ = @(bool, tf) tf{2-bool}();
Questa funzione accetta un valore booleano e un array di celle di due funzioni. La prima di queste funzioni viene valutata se il valore booleano viene valutato come true e il secondo se il valore booleano viene valutato come falso. Ora possiamo facilmente scrivere la funzione fattoriale:
fac = @(n,f) if_(n>1, {@()n*f(n-1,f), @()1});
Il problema qui è che non possiamo invocare direttamente una chiamata ricorsiva, poiché la funzione non è ancora assegnata a una variabile quando viene valutata la parte destra. Possiamo tuttavia completare questo passaggio scrivendo
factorial_ = @(n)fac(n,fac);
Ora @(n)fac(n,fac)
eva ricorsivamente la funzione fattoriale. Un altro modo per farlo nella programmazione funzionale usando un combinatore y, che può anche essere facilmente implementato:
y_ = @(f)@(n)f(n,f);
Con questo strumento, la funzione fattoriale è ancora più breve:
factorial_ = y_(fac);
O direttamente:
factorial_ = y_(@(n,f) if_(n>1, {@()n*f(n-1,f), @()1}));
Salva più figure nello stesso file .fig
Inserendo più handle di figure in una matrice grafica, è possibile salvare più figure nello stesso file .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);
Questo crea 3 scatterplot di dati casuali, ogni parte dell'array grafico h. Quindi l'array grafico può essere salvato usando savefig come con una figura normale, ma con l'handle dell'array grafico come argomento aggiuntivo.
Una nota interessante è che le figure tenderanno a rimanere disposte nello stesso modo in cui sono state salvate quando le apri.
Blocchi di commento
Se vuoi commentare parte del tuo codice, i blocchi di commento possono essere utili. Il blocco dei commenti inizia con %{
in una nuova riga e termina con %}
in un'altra nuova riga:
a = 10;
b = 3;
%{
c = a*b;
d = a-b;
%}
Questo ti permette di piegare le sezioni che hai commentato per rendere il codice più pulito e compatto.
Questi blocchi sono anche utili per attivare / disattivare parti del tuo codice. Tutto quello che devi fare per decommentare il blocco è aggiungere un'altra %
prima di strats:
a = 10;
b = 3;
%%{ <-- another % over here
c = a*b;
d = a-b;
%}
A volte vuoi commentare una sezione del codice, ma senza intaccare il suo rientro:
for k = 1:a
b = b*k;
c = c-b;
d = d*c;
disp(b)
end
Di solito, quando contrassegni un blocco di codice e premi Ctrl + r per commentarlo (aggiungendo il %
automaticamente a tutte le linee, poi quando premi Ctrl + i per il rientro automatico, il blocco di codice si sposta dalla sua gerarchia corretta posto e spostato troppo a destra:
for k = 1:a
b = b*k;
% c = c-b;
% d = d*c;
disp(b)
end
Un modo per risolverlo è usare i blocchi di commento, così la parte interna del blocco rimane correttamente rientrata:
for k = 1:a
b = b*k;
%{
c = c-b;
d = d*c;
%}
disp(b)
end