Jak mogę zindeksować tablicę MATLAB zwróconą przez funkcję bez uprzedniego przypisania jej do zmiennej lokalnej?

363

Na przykład, jeśli chcę odczytać środkową wartość magic(5), mogę to zrobić w następujący sposób:

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

dostać value == 13. Chciałbym móc zrobić coś takiego:

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

zrezygnować ze zmiennej pośredniej. Jednak MATLAB narzeka Unbalanced or unexpected parenthesis or bracketna pierwszy nawias przed 3.

Czy możliwe jest odczytanie wartości z tablicy / macierzy bez uprzedniego przypisania jej do zmiennej?

Joe Kearney
źródło
2
Znalazłem również następujący artykuł na ten temat: mathworks.com/matlabcentral/newsreader/view_thread/280225 Czy ktoś ma nowe informacje na ten temat, czy zostanie ono wdrożone?
2
Ta składnia faktycznie działa dobrze w Octave. Ten problem odkryłem tylko wtedy, gdy moi koledzy używający MATLAB mieli problemy z uruchomieniem mojego kodu.
sffc
2
MATLAB w pigułce.
user76284
1
Ekstrakcja rekurencyjna działa również w Scilab ( scilab.org ) od wersji 6.
Stéphane Mottelet
zarówno testmatrix('magi', 5)(3, 3)na Scilabie, jak i magic(5)(3, 3)na Oktawie działają jak urok!
Foad

Odpowiedzi:

384

Możliwe jest robienie tego, co chcesz, ale musisz użyć funkcjonalnej formy operatora indeksowania. Podczas wykonywania operacji indeksowania za pomocą ()faktycznie wywołujesz subsreffunkcję. Więc nawet jeśli nie możesz tego zrobić:

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

Możesz to zrobić:

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

Brzydkie, ale możliwe. ;)

Ogólnie rzecz biorąc, wystarczy zmienić krok indeksowania na wywołanie funkcji, aby nie było dwóch zestawów nawiasów następujących po sobie. Innym sposobem na to byłoby zdefiniowanie własnej anonimowej funkcji do indeksowania w indeksie dolnym. Na przykład:

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

Jednak kiedy wszystko zostanie powiedziane i zrobione, rozwiązanie tymczasowej zmiennej lokalnej jest znacznie bardziej czytelne i zdecydowanie to, co sugerowałbym.

gnovice
źródło
26
co wiesz! chociaż zgadzam się, że jest to dość brzydkie i prawdopodobnie mniej czytelne niż rozwiązanie temp-var. +1 za imponującą, niejasną wiedzę Matlaba!
drugi
57
To obrzydliwe, ale bardzo jasna odpowiedź. Dobra robota! Powinienem był się domyślić, że będzie w tym droga powrotna. Pomyślę, że będę kontynuować ze zmienną temp.
Joe Kearney,
29
Pamiętaj jednak, że zmienna pośrednia jest nadal w pełni tworzona. Więc jeśli celem jest zaoszczędzenie pamięci, bez konieczności tworzenia tymczasowej zmiennej lokalnej, nie będzie szczęścia.
Sam Roberts,
8
@SamRoberts: Naprawdę nie da się tego obejść w języku oceniającym, takim jak Matlab. Głównym powodem, dla którego ludzie tego chcą, jest zwięzłość / czytelność, a nie oszczędność pamięci.
Ślimak mechaniczny
5
@SamRoberts: To prawda, ale to nie zaoszczędzić od ciężaru dzwoniąc clearna tymczasowy (których nikt nigdy nie) - tymczasowy ma tendencję do trzymać się dłużej
Rody Oldenhuis
131

Kilka dni temu był tylko dobry post na blogu o Lorenie na temat Art of Matlab z kilkoma klejnotami, które mogą pomóc. W szczególności za pomocą funkcji pomocniczych, takich jak:

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

gdzie paren()można użyć jak

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

wróciłby

ans = 16

Przypuszczam również, że będzie to szybsze niż odpowiedź gnovice, ale nie sprawdziłem (użyj profilera !!!). To powiedziawszy, musisz gdzieś dołączyć te definicje funkcji. Osobiście uczyniłem je niezależnymi funkcjami na mojej ścieżce, ponieważ są one bardzo przydatne.

Te i inne funkcje są teraz dostępne w dodatku Functional Programming Constructs, który jest dostępny za pośrednictwem Eksploratora dodatków MATLAB lub wymiany plików .

T. Furfaro
źródło
2
Jest to nieco bardziej ogólna wersja drugiej połowy odpowiedzi gnovice; także dobre.
Joe Kearney
Co myfunc().attr?
gerrit
@gerrit, w czym pomaga? a pole x.attr () nie jest dostępne, chyba że masz przybornik bazy danych.
T. Furfaro
@ T.Furfaro Huh? Jeśli myfunc()zwróci strukturę zawierającą atrybut attr, to aby uzyskać dostęp attrobecnie, muszę to zrobić S = myfunc(); S.attr. Pytanie brzmi, czy możemy mieć funkcję pomocnika getattr(myfunc(), 'attr')analogicznie do pomocników pareni curly. Nie rozumiem, co to ma wspólnego z przybornikiem bazy danych.
gerrit
2
@gerrit Przepraszamy, całkowite zamieszanie (nie wiedziałem, że twoje „attr” było arbitralne - w db tb zdefiniowano taką eksplozję pola). Wierzę, że to, czego szukasz, to getfield ()
T. Furfaro
75

Co sądzisz o korzystaniu z nieudokumentowanych funkcji:

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

lub w przypadku tablic komórkowych:

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

Tak jak magia :)


AKTUALIZACJA:

Złe wieści, powyższy hack już nie działa w R2015b ! W porządku, to była nieudokumentowana funkcjonalność i nie możemy polegać na niej jako obsługiwanej funkcji :)

Jeśli zastanawiasz się, gdzie znaleźć tego rodzaju rzeczy, zajrzyj do folderu fullfile(matlabroot,'bin','registry'). Jest tam kilka plików XML z listą różnego rodzaju gadżetów. Ostrzegamy, że bezpośrednie wywołanie niektórych z tych funkcji może łatwo zawiesić sesję MATLAB.

Amro
źródło
@RodyOldenhuis: Nie pamiętam teraz, chyba musiałem to przeczytać w jakimś zakopanym kodzie;)
Amro
2
':'Aby uniknąć błędu, należy użyć operatora dwukropka (:) z apostrofami Undefined function or variable "builtin".
Dominik,
@Dominik: tak, powiedz, że chcesz wyciąć drugą kolumnę, builtin('_paren', magic(5), ':', 2)to znaczy : (w niektórych miejscach działa to bez cudzysłowów bezpośrednio, :w przeciwieństwie do ':', na przykład podczas uruchamiania w wierszu polecenia bezpośrednio, nie z wnętrza funkcji. to błąd w analizatorze!)
Amro,
2
Nie sądzę, żeby można było endz tym skorzystać?
knedlsepp
2
@knedlsepp: Nie, niestety, cała ta endsztuczka nie działa w tej składni, musisz być jawny w indeksowaniu .. (To samo ograniczenie dotyczy większości innych wymienionych odpowiedzi)
Amro
54

Przynajmniej w MATLAB 2013a możesz używać getfieldtakich jak:

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

uzyskać element w (1,2)

Ian M. García
źródło
5
To właściwie fajna metoda. Jakieś wady?
mmumboss,
6
@mmumboss: To nieudokumentowane zachowanie, ta funkcjonalność może zniknąć bez powiadomienia w przyszłych wersjach. Poza tym żadnych wad.
Daniel
6
Od MATLAB2017b ta funkcja jest udokumentowana.
njspeer
15

niestety składnia like magic(5)(3,3)nie jest obsługiwana przez matlab. musisz użyć tymczasowych zmiennych pośrednich. możesz zwolnić pamięć po użyciu, np

tmp = magic(3);
myVar = tmp(3,3);
clear tmp
druga
źródło
12

Zauważ, że jeśli porównasz czasy działania ze standardowym sposobem (przypisz wynik, a następnie uzyskaj dostęp do wpisów), będą one dokładnie takie same.

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

Moim zdaniem, podstawową kwestią jest: MATLAB nie ma wskaźników, musisz z tym żyć.

Tytus
źródło
6

Może być prostsze, jeśli utworzysz nową funkcję:

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

a następnie użyj go:

value = getElem(magic(5), 3, 3);
Vugar
źródło
1
ale właśnie to subrefrobi ... ale w bardziej ogólny sposób.
Shai
2
tak, bardziej ogólny sposób, ale nie przyjazny ... zbyt brzydki moim zdaniem.
Vugar
4

Twoja początkowa notacja jest najbardziej zwięzłym sposobem:

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

Jeśli robisz to w pętli, możesz po prostu ponownie przypisać M za każdym razem i zignorować również jasne polecenie.

Andreas GS
źródło
6
Zgadzam się, że jest to bardziej zwięzłe, a czyszczenie jest dobrym pomysłem w pętli, jak mówisz, ale pytanie dotyczyło konkretnie, czy można uniknąć przypisania pośredniego.
Joe Kearney
1

Aby uzupełnić odpowiedź Amro, możesz użyć fevalzamiast builtin. Tak naprawdę nie ma różnicy, chyba że spróbujesz przeciążyć funkcję operatora:

BUILTIN (...) jest taki sam jak FEVAL (...) poza tym, że wywoła oryginalną wbudowaną wersję funkcji, nawet jeśli istnieje przeciążona (aby działało, nigdy nie należy przeciążać BUILTIN).

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

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

Interesujące jest to, że fevalwydaje się to nieco szybsze niż builtin(o ~ 3,5%), przynajmniej w Matlabie 2013b, co jest dziwne, biorąc pod uwagę, że fevalnależy sprawdzić, czy funkcja jest przeciążona, w przeciwieństwie do 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.
nirvana-msu
źródło
W rzeczywistości nie jest to dziwne: MATLAB prowadzi listę zdefiniowanych funkcji, nie ma zbyt wiele do szukania. fevalrobi „normalną” rzecz i dlatego może w pełni wykorzystać tę listę. builtinmusi szukać gdzie indziej, aby znaleźć tylko wbudowane funkcje. Prawdopodobnie ten przypadek nie jest zoptymalizowany prawie tak bardzo, jak przypadek „normalny”, ponieważ dlaczego miałbyś wkładać pieniądze w optymalizację czegoś, co nie jest często używane?
Cris Luengo