Jak powrócisz z funkcji w dowolnym momencie?

12

Jak wrócić wcześniej z funkcji przed jej zakończeniem? Na przykład:

(defun my-func () 
 "for example."
 (unless something (return nil))
 ; continue as usual...
 (+ 42 1))
ocodo
źródło

Odpowiedzi:

19

Mamy wiele dostępnych opcji.

Rzucać

Możesz catch/ throwwyjść z funkcji.

przykład:

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

Blok

Możesz także użyć blocki return-from(chociaż będziesz musiał wymagać cl-macs)

przykład:

(require 'cl-macs)

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

cl-defun

Mamy też cl-defuncoś, co ma domyślną blocknazwę o tej samej nazwie co funkcja, więc możemy wykonać blockstyl za mniej.

przykład:

(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)))

odrzucić *

cl-defunjest również dostępny jako alias defun*zdefiniowany w następujący cl.elsposób:

(require 'cl)

(defun* my-func ()
  "defun* implicit block"
  (when t
    (return-from my-func)) ; my-func is an implicit block.
  (+ 42 1)))
ocodo
źródło
4
Zauważ, że jeśli nie preferujesz składni CL, catch/ throwjest bardziej idiomatyczny w elisp, ponieważ inne podejścia są ostatecznie implementowane w kategoriach catch / throw. Elisp instrukcja mówi: „Większość innych wersjach Lisp, w tym Common Lisp, mają kilka sposobów przekazywania sterowania nonsequentially: return, return-from, i go., Na przykład Emacs Lisp ma tylko throw”.
phils
5

Oprócz tego, co obejmuje @EmacsFodder, po prostu zgłoś błąd.

Nie pomoże to, jeśli kod zostanie wywołany (dynamicznie, nie leksykalnie) w zakresie konstrukcji obsługujących błędy, takich jak ignore-errorslub condition-case, ale w przeciwnym razie jest to dobry sposób na wyjście z funkcji. W rzeczywistości tak się dzieje przez większość czasu.

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

Jeśli chcesz sam poradzić sobie z błędem, możesz umieścić kod wywołujący (np. Wywołanie czegoś, co ostatecznie wywołuje my-func) wewnątrz condition-case. Znowu tak się dzieje przez większość czasu, przynajmniej tak często, jak przy użyciu catch+ throw. Wszystko zależy od tego, jakie zachowanie chcesz.

Rysował
źródło
Dzięki za odpowiedź Drew, zgadzam się, że jest to dość powszechna metoda. Jednak samo wykonanie wczesnego powrotu w wielu innych językach nie powoduje złożoności konieczności radzenia sobie z błędem. Podczas wyszukiwania zestawu pytań / odpowiedzi. W szczególności szukałem alternatyw dla stylu „popełniania błędów”, który zawsze wydaje mi się niechlujny. Nie sprecyzowałem tego wprost w tekście pytania.
ocodo
1
Wszystko zależy od tego, co chcesz zrobić. Jeśli chcesz zakończyć natychmiast, bez dalszego przetwarzania / obsługi, wtedy zgłoszenie błędu jest dobrym sposobem na nielokalne wyjście w Emacsie. Jeśli chcesz coś zrobić podczas nielokalnego wyjścia, czyli obsługiwać go w jakiś sposób, a następnie catch, unwind-protect, condition-casei tym podobne są użyteczne. Istnieje cała sekcja podręcznika Elisp poświęcona nielokalnym wyjściom . (I w żadnym z nich nie ma nic szczególnie nieprzyzwoitego, IMO.)
Drew
„Czuje” jest oczywiście całkowicie subiektywne. Dzięki za nielokalny podręcznik ref.
ocodo