Bagaimana Anda kembali dari suatu fungsi pada titik arbitrer?

12

Bagaimana Anda kembali lebih awal dari suatu fungsi sebelum berakhir? Sebagai contoh:

(defun my-func () 
 "for example."
 (unless something (return nil))
 ; continue as usual...
 (+ 42 1))
ocodo
sumber

Jawaban:

19

Kami memiliki sejumlah opsi yang tersedia.

Melemparkan

Anda dapat catch/ throwuntuk keluar dari fungsi.

contoh:

(defun my-func ()
  "thrown error"
  (catch 'my-catch
    (when t
      (throw 'my-catch "always going to throw"))
    (+ 42 1)))

Blok

Anda juga dapat menggunakan blockdan return-from(meskipun Anda harus meminta cl-macs)

contoh:

(require 'cl-macs)

(defun my-func ()
  "block / return-from"
  (block my-func
    (when t
      (return-from my-func))
    (+ 42 1)))

cl-defun

Kami juga memiliki cl-defunyang memiliki implisit blockdengan nama yang sama dengan fungsi, sehingga kami dapat melakukan blockgaya dengan lebih sedikit.

contoh:

(require 'cl-macs)

(cl-defun my-func ()
  "cl-defun implicit block"
  (when t
    (return-from my-func)) ; my-func is an implicit block.
  (+ 42 1)))

menantang *

cl-defunjuga tersedia sebagai alias defun*yang didefinisikan sebagai cl.el:

(require 'cl)

(defun* my-func ()
  "defun* implicit block"
  (when t
    (return-from my-func)) ; my-func is an implicit block.
  (+ 42 1)))
ocodo
sumber
4
Perhatikan bahwa jika Anda tidak memiliki preferensi untuk sintaks CL, catch/ throwitu lebih idiomatis di elisp, karena pendekatan lain pada akhirnya diimplementasikan dalam hal menangkap / melempar. Manual elisp mengatakan: "Kebanyakan versi lain dari Lisp, termasuk Common Lisp, memiliki beberapa cara untuk mentransfer kontrol nonsequentially: return, return-from, dan go., Misalnya Emacs Lisp hanya memiliki throw."
phils
5

Selain apa yang dicakup @EmacsFodder, cukup ajukan kesalahan.

Ini tidak akan membantu jika kode dipanggil di dalam (secara dinamis, bukan secara leksikal) sejauh mana konstruksi penanganan kesalahan seperti ignore-errorsatau condition-case, tetapi sebaliknya itu adalah cara yang baik untuk keluar dari suatu fungsi. Sebenarnya apa yang dilakukan sebagian besar waktu.

(defun my-func () 
 "..."
 (unless something (error "Whoops!"))
 ; continue as usual...
 (+ 42 1))

Jika Anda ingin menangani sendiri kesalahannya maka Anda dapat memasukkan kode panggilan (mis. Panggilan ke sesuatu yang memanggil secara berlebihan my-func) di dalam a condition-case. Sekali lagi, inilah yang paling sering dilakukan, paling tidak sesering menggunakan catch+ throw. Itu semua tergantung pada perilaku apa yang Anda inginkan.

Drew
sumber
Terima kasih atas jawabannya Drew, saya setuju ini adalah metode yang cukup umum. Namun, hanya melakukan pengembalian awal dalam banyak bahasa lain, tidak menimbulkan kerumitan karena harus kemudian berurusan dengan kesalahan. Saat meneliti set pertanyaan / jawaban. Saya secara khusus mencari alternatif untuk gaya "kesalahan" yang selalu terasa muram bagi saya. Saya tidak menentukan ini secara eksplisit dalam teks pertanyaan yang membuat Anda keberatan.
ocodo
1
Itu semua tergantung pada apa yang ingin Anda lakukan. Jika Anda ingin segera mengakhiri, tanpa pemrosesan / penanganan lebih lanjut, maka meningkatkan kesalahan adalah cara yang baik untuk keluar secara non-lokal, di Emacs. Jika Anda ingin melakukan sesuatu selama keluar nonlokal, yaitu, mengatasinya dalam beberapa cara, maka catch, unwind-protect, condition-casedan sejenisnya yang berguna. Ada seluruh bagian dari manual Elisp yang dikhususkan untuk keluar nonlokal . (Dan tidak ada yang khusus tentang mereka, IMO.)
Drew
"Terasa" sepenuhnya subjektif tentu saja. Terima kasih untuk referensi manual nonlokal.
ocodo