Come posso indicizzare un array MATLAB restituito da una funzione senza prima assegnarlo a una variabile locale?


363

Ad esempio, se voglio leggere il valore medio da magic(5), posso farlo in questo modo:

M = magic(5);
value = M(3,3);

per ottenere value == 13. Mi piacerebbe poter fare qualcosa di simile a uno di questi:

value = magic(5)(3,3);
value = (magic(5))(3,3);

rinunciare alla variabile intermedia. Tuttavia, MATLAB si lamenta Unbalanced or unexpected parenthesis or bracketdella prima parentesi prima della 3.

È possibile leggere i valori da un array / matrice senza prima assegnarlo a una variabile?


2
Ho anche trovato il seguente articolo su questo tema: mathworks.com/matlabcentral/newsreader/view_thread/280225 Qualcuno ha nuove informazioni su questo tema, sarà implementato?

2
Questa sintassi funziona davvero bene in Octave. Ho scoperto questo problema solo quando i miei colleghi che usano MATLAB hanno avuto problemi con il mio codice.
Sffc,

2
MATLAB in breve.
user76284

1
L'estrazione ricorsiva funziona anche in Scilab ( scilab.org ) dalla versione 6.
Stéphane Mottelet,

sia testmatrix('magi', 5)(3, 3)su Scilab che magic(5)(3, 3)su Octave funzionano entrambi come un incantesimo!
Foad,

Risposte:


384

In realtà è possibile fare quello che vuoi, ma devi usare la forma funzionale dell'operatore di indicizzazione. Quando si esegue un'operazione di indicizzazione mediante (), si sta effettivamente effettuando una chiamata alla subsreffunzione. Quindi, anche se non puoi farlo:

value = magic(5)(3, 3);

È possibile fare questo:

value = subsref(magic(5), struct('type', '()', 'subs', {{3, 3}}));

Brutto, ma possibile. ;)

In generale, è sufficiente modificare il passaggio di indicizzazione in una chiamata di funzione in modo da non avere due serie di parentesi immediatamente successive l'una all'altra. Un altro modo per farlo sarebbe definire la propria funzione anonima per eseguire l'indicizzazione sottoscritta. Per esempio:

subindex = @(A, r, c) A(r, c);     % An anonymous function for 2-D indexing
value = subindex(magic(5), 3, 3);  % Use the function to index the matrix

Tuttavia, quando tutto è stato detto e fatto, la soluzione di variabile locale temporanea è molto più leggibile, e sicuramente ciò che suggerirei.


26
bene cosa sai! anche se sono d'accordo è piuttosto brutto, e probabilmente meno leggibile di una soluzione temp-var. +1 per una conoscenza oscura impressionante del matlab!
secondo

57
È disgustoso, ma una risposta molto chiara. Buon lavoro! Avrei dovuto indovinare che ci sarebbe stato un passo indietro. Penserò che continuerò con la variabile temp.
Joe Kearney,

29
Tieni presente che la variabile intermedia è comunque completamente creata. Quindi, se lo scopo è quello di risparmiare memoria, non dovendo creare una variabile locale temporanea, senza fortuna.
Sam Roberts,

8
@SamRoberts: non puoi davvero evitarlo in un linguaggio di valutazione rigorosa come Matlab. Il motivo principale per cui le persone lo desiderano è la concisione / leggibilità, non il risparmio di memoria.
Lumaca meccanica,

5
@SamRoberts: vero, ma fa risparmiare dal peso di chiamare clearsul temporanea (che nessuno fa mai) - la temporanea tende a restare più a lungo
Rody Oldenhuis

131

C'era solo un buon post sul blog su Loren sull'Arte di Matlab un paio di giorni fa con un paio di gemme che potrebbero aiutare. In particolare, l'utilizzo di funzioni di supporto come:

paren = @(x, varargin) x(varargin{:});
curly = @(x, varargin) x{varargin{:}};

dove paren()può essere usato come

paren(magic(5), 3, 3);

sarebbe tornato

ans = 16

Suppongo anche che questo sarà più veloce della risposta di Gnovice, ma non ho controllato (Usa il profiler !!!). Detto questo, devi anche includere queste definizioni delle funzioni da qualche parte. Personalmente li ho resi funzioni indipendenti nel mio percorso, perché sono super utili.

Queste e altre funzioni sono ora disponibili nel componente aggiuntivo Funzionalità di programmazione funzionale disponibile tramite l'Explorer componenti aggiuntivi MATLAB o lo scambio di file .


2
Questa è una versione leggermente più generale della seconda metà della risposta di Gnovice; anche buono.
Joe Kearney,

Che dire myfunc().attr?
Gerrit,

@gerrit, come aiuta? e il campo x.attr () non è disponibile a meno che non si disponga della casella degli strumenti del database.
T. Furfaro,

@ T.Furfaro Huh? Se myfunc()restituisce una struttura che include un attributo attr, per accedere al attrmomento devo farlo S = myfunc(); S.attr. La domanda è se possiamo avere una funzione di supporto come getattr(myfunc(), 'attr')in analogia con gli parene curlyaiutanti. Non capisco cosa abbia a che fare con la cassetta degli attrezzi del database.
Gerrit,

2
@gerrit Siamo spiacenti, totale confusione (non ero a conoscenza del fatto che il tuo "attr" fosse arbitrario - nel db tb è definita una tale esplicità di campo). Credo che quello che stai cercando sia getfield ()
T. Furfaro il

75

Cosa ne pensi dell'utilizzo di funzionalità non documentate:

>> builtin('_paren', magic(5), 3, 3)               %# M(3,3)
ans =
    13

o per array di celle:

>> builtin('_brace', num2cell(magic(5)), 3, 3)     %# C{3,3}
ans =
    13

Proprio come per magia :)


AGGIORNARE:

Cattive notizie, l'hack sopra non funziona più in R2015b ! Va bene, era funzionalità non documentata e non possiamo fare affidamento su di essa come funzionalità supportata :)

Per coloro che si chiedono dove trovare questo tipo di cose, guarda nella cartella fullfile(matlabroot,'bin','registry'). C'è un sacco di file XML lì che elencano tutti i tipi di chicche. Tieni presente che la chiamata diretta di alcune di queste funzioni può causare l'arresto anomalo della sessione MATLAB.


@RodyOldenhuis: non ricordo ora, credo di averlo letto in un codice sepolto;)
Amro,

2
L'operatore due punti (:) deve essere utilizzato con apostrofi ':'per evitare l'errore Undefined function or variable "builtin".
Dominik,

@Dominik: giusto, diciamo che vuoi tagliare la seconda colonna, che sarebbe: builtin('_paren', magic(5), ':', 2)(in alcuni punti funziona senza le virgolette direttamente :al contrario ':', come quando si esegue direttamente nel prompt dei comandi non dall'interno di una funzione. questo è un bug nel parser!)
Amro

2
Suppongo che non ci sia un modo per usarlo end?
Knedlsepp,

2
@knedlsepp: No, sfortunatamente tutta la endtrappola non funziona in questa sintassi, dovrai essere esplicito nella tua indicizzazione .. (La stessa limitazione si applica alla maggior parte delle altre risposte elencate)
Amro

54

Almeno in MATLAB 2013a puoi usare getfieldcome:

a=rand(5);
getfield(a,{1,2}) % etc

per ottenere l'elemento in (1,2)


5
Questo è in realtà un bel metodo. Qualche inconveniente?
mmumboss

6
@mmumboss: questo è un comportamento non documentato, questa funzionalità potrebbe scomparire senza preavviso nelle versioni future. Oltre a questo senza svantaggi.
Daniel,

6
A partire da MATLAB2017b, questa funzionalità è documentata.
njspeer

15

purtroppo la sintassi come magic(5)(3,3)non è supportata da matlab. è necessario utilizzare variabili intermedie temporanee. è possibile liberare memoria dopo l'uso, ad es

tmp = magic(3);
myVar = tmp(3,3);
clear tmp

12

Si noti che se si confrontano i tempi di esecuzione con il modo standard (assegnare il risultato e quindi accedere alle voci), sono esattamente gli stessi.

subs=@(M,i,j) M(i,j);
>> for nit=1:10;tic;subs(magic(100),1:10,1:10);tlap(nit)=toc;end;mean(tlap)

ans =

0.0103

>> for nit=1:10,tic;M=magic(100); M(1:10,1:10);tlap(nit)=toc;end;mean(tlap)

ans =

0.0101

A mio avviso, la linea di fondo è: MATLAB non ha puntatori, devi conviverci.


6

Potrebbe essere più semplice se si effettua una nuova funzione:

function [ element ] = getElem( matrix, index1, index2 )
    element = matrix(index1, index2);
end

e poi usalo:

value = getElem(magic(5), 3, 3);

1
ma questo è esattamente ciò che subreffa ... ma in un modo più generale.
Shai,

2
sì, in modo più generale, ma non amichevole ... a molto brutto secondo me.
Vugar

4

La tua notazione iniziale è il modo più conciso per farlo:

M = magic(5);  %create
value = M(3,3);  % extract useful data
clear M;  %free memory

Se lo stai facendo in un ciclo, puoi semplicemente riassegnare M ogni volta e ignorare anche la frase chiara.


6
Concordo sul fatto che ciò sia più conciso e che la compensazione sia una buona idea in un ciclo, come dici tu, ma la domanda era specificamente se l'incarico intermedio potesse essere evitato.
Joe Kearney,

1

Per completare la risposta di Amro, puoi usare fevalinvece di builtin. Non c'è differenza, davvero, a meno che non si provi a sovraccaricare la funzione operatore:

BUILTIN (...) è lo stesso di FEVAL (...) tranne per il fatto che chiamerà la versione originale incorporata della funzione anche se ne esiste una sovraccarica (perché funzioni, non devi mai sovraccaricare BUILTIN).

>> feval('_paren', magic(5), 3, 3)               % M(3,3)
ans =
    13

>> feval('_brace', num2cell(magic(5)), 3, 3)     % C{3,3}
ans =
    13

La cosa interessante è che fevalsembra essere leggermente più veloce di builtin(del ~ 3,5%), almeno in Matlab 2013b, il che è strano dato che fevaldeve controllare se la funzione è sovraccarica, a differenza di builtin:

>> tic; for i=1:1e6, feval('_paren', magic(5), 3, 3); end; toc;
Elapsed time is 49.904117 seconds.
>> tic; for i=1:1e6, builtin('_paren', magic(5), 3, 3); end; toc;
Elapsed time is 51.485339 seconds.

In realtà non è strano: MATLAB mantiene un elenco di funzioni definite, non c'è molto da cercare. fevalfa la cosa "normale" e quindi può sfruttare appieno questo elenco. builtindeve cercare altrove in modo da trovare solo funzioni integrate. Probabilmente questo caso non è ottimizzato quasi quanto il caso "normale", perché perché dovresti investire nell'ottimizzare qualcosa che non viene usato molto spesso?
Cris Luengo,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.