Bagaimana saya bisa mengindeks array MATLAB yang dikembalikan oleh fungsi tanpa terlebih dahulu menetapkannya ke variabel lokal?

363

Misalnya, jika saya ingin membaca nilai tengah dari magic(5), saya bisa melakukannya seperti ini:

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

untuk mendapatkan value == 13. Saya ingin dapat melakukan sesuatu seperti ini:

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

untuk membuang variabel perantara. Namun, MATLAB mengeluh tentang Unbalanced or unexpected parenthesis or bracketkurung pertama sebelum 3.

Apakah mungkin untuk membaca nilai dari array / matriks tanpa terlebih dahulu menugaskannya ke variabel?

Joe Kearney
sumber
2
Saya juga menemukan artikel berikut tentang tema ini: mathworks.com/matlabcentral/newsreader/view_thread/280225 Siapa saja yang memiliki informasi baru tentang tema ini, apakah ini akan diterapkan?
2
Sintaks ini sebenarnya berfungsi dengan baik di Oktaf. Saya hanya menemukan masalah ini ketika kolega saya yang menggunakan MATLAB mengalami masalah dalam menjalankan kode saya.
sffc
2
Singkatnya, MATLAB.
user76284
1
Ekstraksi rekursif juga bekerja di Scilab ( scilab.org ) sejak versi 6.
Stéphane Mottelet
yang testmatrix('magi', 5)(3, 3)pada Scilab danmagic(5)(3, 3) pada Oktaf berdua bekerja seperti pesona!
Isi

Jawaban:

384

Ini sebenarnya adalah mungkin untuk melakukan apa yang Anda inginkan, tetapi Anda harus menggunakan bentuk fungsional dari operator pengindeksan. Saat Anda melakukan operasi pengindeksan menggunakan (), Anda sebenarnya membuat panggilan ke subsreffungsi. Jadi, meskipun Anda tidak dapat melakukan ini:

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

Anda bisa melakukan ini:

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

Jelek, tapi mungkin. ;)

Secara umum, Anda hanya perlu mengubah langkah pengindeksan menjadi panggilan fungsi sehingga Anda tidak memiliki dua set tanda kurung segera mengikuti satu sama lain. Cara lain untuk melakukan ini adalah dengan mendefinisikan fungsi anonim Anda sendiri untuk melakukan pengindeksan subskrip. Sebagai contoh:

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

Namun, ketika semua dikatakan dan dilakukan solusi variabel lokal sementara jauh lebih mudah dibaca, dan pasti apa yang akan saya sarankan.

gnovice
sumber
26
baik apa yang kamu tahu! meskipun saya setuju itu cukup jelek, dan mungkin kurang mudah dibaca daripada solusi temp-var. +1 untuk pengetahuan matlab jelas yang mengesankan!
kedua
57
Itu menjijikkan, tetapi jawaban yang sangat jelas. Kerja bagus! Seharusnya bisa menebak akan ada jalan kembali ke dalamnya. Saya akan berpikir saya akan melanjutkan dengan variabel temp.
Joe Kearney
29
Ingatlah bahwa variabel perantara masih sepenuhnya dibuat. Jadi jika tujuannya adalah untuk menghemat memori dengan tidak harus membuat variabel lokal sementara, tidak berhasil.
Sam Roberts
8
@ SamRoberts: Anda tidak dapat benar-benar menyiasatinya dalam bahasa evaluasi ketat seperti Matlab. Alasan utama orang menginginkan ini adalah keringkasan / keterbacaan, bukan penghematan memori.
Siput mekanik
5
@SamRoberts: benar, tetapi itu tidak menyelamatkan Anda dari beban memanggil clearsementara (yang tidak seorang pun pernah melakukannya) - sementara cenderung bertahan lebih lama
Rody Oldenhuis
131

Hanya ada posting blog yang bagus di Loren pada Seni Matlab beberapa hari yang lalu dengan beberapa permata yang mungkin membantu. Secara khusus, menggunakan fungsi pembantu seperti:

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

dimana paren()bisa dipakai suka

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

akan kembali

ans = 16

Saya juga menduga bahwa ini akan lebih cepat daripada jawaban gnovice, tetapi saya belum memeriksa (Gunakan profiler !!!). Yang sedang berkata, Anda juga harus memasukkan definisi fungsi ini di suatu tempat. Saya pribadi telah menjadikannya fungsi independen di jalur saya, karena mereka sangat berguna.

Fungsi-fungsi ini dan lainnya sekarang tersedia di add-on Konstruk Pemrograman Fungsional yang tersedia melalui MATLAB Add-On Explorer atau di File Exchange .

T. Furfaro
sumber
2
Ini adalah versi yang sedikit lebih umum dari jawaban gnovice paruh kedua; juga bagus.
Joe Kearney
Bagaimana dengan myfunc().attr?
gerrit
@gerrit, bagaimana caranya membantu? dan bidang x.attr () tidak tersedia kecuali Anda memiliki kotak alat basis data.
T. Furfaro
@ T.Furfaro Huh? Jika myfunc()mengembalikan struktur yang menyertakan atribut attr, maka untuk mengakses attrsaat ini perlu saya lakukan S = myfunc(); S.attr. Pertanyaannya adalah apakah kita dapat memiliki fungsi pembantu seperti getattr(myfunc(), 'attr')analogi dengan parendan curlypembantu. Saya tidak mengerti apa hubungannya dengan kotak alat basis data.
gerrit
2
@ kritikus Maaf, kebingungan total (saya tidak tahu bahwa "attr" Anda sewenang-wenang - di db tb ada kejelasan bidang yang ditentukan). Saya yakin yang Anda cari adalah getfield ()
T. Furfaro
75

Bagaimana perasaan Anda tentang menggunakan fitur tidak berdokumen:

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

atau untuk array sel:

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

Sama seperti sihir :)


MEMPERBARUI:

Berita buruk, peretasan di atas tidak berfungsi lagi di R2015b ! Tidak apa-apa, itu fungsionalitas tidak berdokumen dan kami tidak dapat mengandalkannya sebagai fitur yang didukung :)

Bagi mereka yang bertanya-tanya di mana menemukan hal semacam ini, lihat di folder fullfile(matlabroot,'bin','registry'). Ada banyak file XML di sana yang mencantumkan semua jenis barang. Berhati-hatilah bahwa memanggil beberapa fungsi ini secara langsung dapat dengan mudah membuat crash sesi MATLAB Anda.

Amro
sumber
@RodyOldenhuis: Saya tidak ingat sekarang, saya kira saya pasti sudah membacanya dalam beberapa kode terkubur;)
Amro
2
Operator titik dua (:) harus digunakan dengan tanda kutip ':'untuk menghindari kesalahan Undefined function or variable "builtin".
Dominik
@Dominik: benar, katakan Anda ingin mengiris kolom ke-2, yaitu: builtin('_paren', magic(5), ':', 2)(di tempat-tempat tertentu itu berfungsi tanpa kutipan secara langsung :sebagai lawan ':', seperti ketika menjalankan command prompt langsung bukan dari dalam fungsi. Saya kira itu bug di parser!)
Amro
2
Saya kira tidak ada cara untuk menggunakannya end?
knedlsepp
2
@knedlsepp: Tidak, sayangnya keseluruhan end-trickery tidak berfungsi dalam sintaks ini, Anda harus eksplisit dalam pengindeksan Anda .. (Batasan yang sama berlaku untuk sebagian besar jawaban terdaftar lainnya)
Amro
54

Setidaknya dalam MATLAB 2013a Anda dapat menggunakan getfieldseperti:

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

untuk mendapatkan elemen di (1,2)

Ian M. García
sumber
5
Ini sebenarnya metode yang bagus. Adakah kekurangannya?
mmumboss
6
@mmumboss: Itu perilaku tidak berdokumen, fungsi ini dapat menghilang tanpa pemberitahuan di versi mendatang. Selain itu tidak ada kerugiannya.
Daniel
6
Pada MATLAB2017b, fungsi ini didokumentasikan.
njspeer
15

sayangnya syntax like magic(5)(3,3)tidak didukung oleh matlab. Anda perlu menggunakan variabel perantara sementara. Anda dapat membebaskan memori setelah digunakan, mis

tmp = magic(3);
myVar = tmp(3,3);
clear tmp
kedua
sumber
12

Perhatikan bahwa jika Anda membandingkan waktu berjalan dengan cara standar (letakkan hasilnya dan kemudian mengakses entri), mereka persis sama.

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

Menurut pendapat saya, intinya adalah: MATLAB tidak memiliki pointer, Anda harus hidup dengannya.

titus
sumber
6

Ini bisa lebih sederhana jika Anda membuat fungsi baru:

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

dan kemudian menggunakannya:

value = getElem(magic(5), 3, 3);
Vugar
sumber
1
tapi inilah tepatnya yang subrefdilakukan ... tetapi dengan cara yang lebih umum.
Shai
2
ya, cara yang lebih umum, tapi tidak ramah ... terlalu jelek menurut saya.
Vugar
4

Notasi awal Anda adalah cara paling ringkas untuk melakukan ini:

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

Jika Anda melakukan ini dalam satu lingkaran, Anda bisa menetapkan ulang M setiap waktu dan mengabaikan pernyataan yang jelas juga.

Andreas GS
sumber
6
Saya setuju bahwa ini lebih ringkas, dan kliring adalah ide bagus dalam satu lingkaran, seperti yang Anda katakan, tetapi pertanyaannya adalah secara khusus apakah tugas perantara dapat dihindari.
Joe Kearney
1

Untuk melengkapi jawaban Amro, Anda dapat menggunakan fevalbukan builtin. Tidak ada perbedaan, sungguh, kecuali jika Anda mencoba untuk membebani fungsi operator:

BUILTIN (...) sama dengan FEVAL (...) kecuali bahwa itu akan memanggil versi built-in asli dari fungsi bahkan jika ada kelebihan beban (agar ini berfungsi, Anda tidak boleh pernah membebani BUILTIN).

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

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

Yang menarik adalah itu fevaltampaknya sedikit lebih cepat daripada builtin(sekitar ~ 3,5%), setidaknya di Matlab 2013b, yang aneh mengingat bahwa fevalperlu memeriksa apakah fungsinya kelebihan beban, tidak seperti 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
sumber
Sebenarnya tidak aneh: MATLAB menyimpan daftar fungsi yang didefinisikan, tidak ada banyak pencarian yang harus dilakukan. fevalmelakukan hal "normal" dan karenanya dapat menggunakan daftar ini sepenuhnya. builtinharus mencari di tempat lain sehingga hanya menemukan fungsi bawaan. Kemungkinan kasus ini tidak dioptimalkan hampir sebanyak kasus "normal", karena mengapa Anda memasukkan uang ke dalam mengoptimalkan sesuatu yang tidak terlalu sering digunakan?
Cris Luengo