Wie kehren Sie von einer Funktion an einem beliebigen Punkt zurück?

12

Wie können Sie frühzeitig von einer Funktion zurückkehren, bevor sie beendet ist? Beispielsweise:

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

Antworten:

19

Wir haben eine Reihe von Optionen zur Verfügung.

Werfen

Sie können catch/ throwdie Funktion zu verlassen.

Beispiel:

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

Block

Sie können auch blockund verwenden return-from(obwohl Sie benötigen cl-macs)

Beispiel:

(require 'cl-macs)

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

cl-defun

Wir haben cl-defunauch ein implizites Element blockmit demselben Namen wie die Funktion, sodass wir den blockStil mit weniger ausführen können .

Beispiel:

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

defun *

cl-defunist auch als Alias ​​verfügbar, defun*der folgendermaßen definiert ist 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
quelle
4
Beachten Sie, dass catch/ throwin elisp idiomatischer ist , wenn Sie die CL-Syntax nicht bevorzugen , da andere Ansätze letztendlich in Bezug auf catch / throw implementiert werden. Die elisp Handbuch sagt: „ Die meisten anderen Versionen von Lisp, einschließlich Common Lisp, haben mehrere Möglichkeiten der Steuerung übertragen nonsequentially: return, return-from, und go., Zum Beispiel Emacs Lisp hat nur throw.“
Phils
5

Zusätzlich zu dem, was @EmacsFodder behandelt, wird nur ein Fehler gemeldet.

Dies ist nicht hilfreich, wenn der Code innerhalb des Umfangs von Fehlerbehandlungskonstrukten wie ignore-errorsoder (dynamisch, nicht lexikalisch) aufgerufen wird. condition-caseAndernfalls ist dies eine gute Möglichkeit, eine Funktion zu beenden. Es ist in der Tat, was die meiste Zeit getan wird.

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

Wenn Sie den Fehler selbst behandeln möchten, können Sie den aufrufenden Code (z. B. den Anruf an etwas, das letztendlich aufruft my-func) in a einfügen condition-case. Wiederum wird dies die meiste Zeit getan, mindestens so oft wie mit catch+ throw. Es hängt alles davon ab, welches Verhalten Sie wollen.

Drew
quelle
Vielen Dank für die Antwort Drew, ich bin damit einverstanden, dass dies eine weit verbreitete Methode ist. Allerdings nur eine frühe Rückkehr in vielen anderen Sprachen zu tun, entsteht nicht die Komplexität des Müssens dann mit einem Fehler umgehen. Bei der Recherche des Frage- / Antwortsatzes. Ich habe gezielt nach Alternativen zum "erroring out" -Stil gesucht, der sich für mich immer klobig anfühlt. Das habe ich im Fragentext wohlgemerkt nicht explizit angegeben.
1.
1
Es hängt alles davon ab, was Sie tun möchten. Wenn Sie sofort ohne weitere Verarbeitung / Bearbeitung beenden möchten, ist das Auslösen eines Fehlers in Emacs ein guter Weg, um einen nichtlokalen Exit durchzuführen. Wenn Sie während eines nichtlokalen Exits etwas tun möchten, dh auf irgendeine Weise damit umgehen möchten catch, sind unwind-protect, condition-caseund dergleichen nützlich. Es gibt einen ganzen Abschnitt des Elisp-Handbuchs, der sich mit nichtlokalen Ausgängen befasst . (Und es gibt nichts besonders kludiges an einem von ihnen, IMO.)
Drew
"Feels" ist natürlich völlig subjektiv. Vielen Dank für die nicht-lokale manuelle Ref.
ocodo