¿Cómo regresas de una función en un punto arbitrario?

12

¿Cómo regresas temprano de una función antes de que termine? Por ejemplo:

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

Respuestas:

19

Tenemos varias opciones disponibles.

Lanzar

Puede catch/ throwpara salir de la función.

ejemplo:

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

Bloquear

También puede usar blocky return-from(aunque necesitará requerir cl-macs)

ejemplo:

(require 'cl-macs)

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

cl-defun

También tenemos cl-defunun implícito blockcon el mismo nombre que la función, por lo que podemos hacer el blockestilo con menos.

ejemplo:

(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-defuntambién está disponible como un alias defun*que se define cl.elasí:

(require 'cl)

(defun* my-func ()
  "defun* implicit block"
  (when t
    (return-from my-func)) ; my-func is an implicit block.
  (+ 42 1)))
ocodo
fuente
44
Tenga en cuenta que si no tiene preferencia por la sintaxis CL, catch/ throwes más idiomático en elisp, ya que otros enfoques finalmente se implementan en términos de captura / lanzamiento. El manual elisp dice: "La mayoría de las otras versiones de Lisp, incluyendo Common Lisp, tienen varias maneras de transferir el control de forma no secuencial: return, return-from, y go., Por ejemplo Emacs Lisp tiene solamente throw".
phils
5

Además de lo que cubrió @EmacsFodder, solo genera un error.

Esto no ayudará si el código se llama dentro (dinámicamente, no léxicamente) del alcance de las construcciones de manejo de errores como ignore-errorso condition-case, pero de lo contrario es una buena manera de salir de una función. De hecho, es lo que se hace la mayor parte del tiempo.

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

Si desea manejar el error usted mismo, puede poner el código de llamada (por ejemplo, la llamada a algo que finalmente llama my-func) dentro de a condition-case. Nuevamente, esto es lo que se hace la mayor parte del tiempo, al menos tan a menudo como usar catch+ throw. Todo depende de qué comportamiento quieras.

Dibujó
fuente
Gracias por la respuesta Drew, estoy de acuerdo en que este es un método bastante común. Sin embargo, solo hacer un regreso temprano en muchos otros idiomas no implica la complejidad de tener que lidiar con un error. Al investigar el conjunto de preguntas / respuestas. Estaba buscando específicamente alternativas al estilo de "error" que siempre me parece poco claro. No especifiqué esto explícitamente en el texto de la pregunta.
ocodo 01 de
1
Todo depende de lo que quieras hacer. Si desea finalizar de inmediato, sin más procesamiento / manejo, entonces generar un error es una buena forma de salir no localmente, en Emacs. Si usted quiere hacer algo durante una salida no local, es decir, manejar de alguna manera, entonces catch, unwind-protect, condition-casey similares son útiles. Hay una sección completa del manual de Elisp dedicada a las salidas no locales . (Y no hay nada particularmente grosero en ninguno de ellos, OMI.)
Dibujó
"Feels" es completamente subjetivo, por supuesto. Gracias por el manual no local ref.
ocodo 01 de