Como você retorna de uma função em um ponto arbitrário?

12

Como você retorna mais cedo de uma função antes que ela termine? Por exemplo:

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

Respostas:

19

Temos várias opções disponíveis.

Lançar

Você pode catch/ throwpara sair da função.

exemplo:

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

Quadra

Você também pode usar blocke return-from(embora seja necessário cl-macs)

exemplo:

(require 'cl-macs)

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

cl-defun

Também temos cl-defunque tem um implícito blockcom o mesmo nome que a função, para que possamos fazer o blockestilo com menos.

exemplo:

(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-defuntambém está disponível como um alias defun*definido em 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
fonte
4
Observe que, se você não tem preferência pela sintaxe do CL, catch/ throwé mais idiomático no elisp, pois outras abordagens são implementadas em termos de captura / lançamento. O manual elisp diz: "A maioria das outras versões do Lisp, incluindo Lisp comum, tem várias maneiras de transferir o controle nonsequentially: return, return-from, e go., Por exemplo Emacs Lisp tem apenas throw."
Phll
5

Além do que o @EmacsFodder abordou, basta gerar um erro.

Isso não ajudará se o código for chamado dentro (dinamicamente, não lexicamente) da extensão das construções de tratamento de erros como ignore-errorsou condition-case, mas, caso contrário, é uma boa maneira de sair de uma função. Na verdade, é o que é feito na maioria das vezes.

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

Se você quiser lidar com o erro você mesmo, poderá colocar o código de chamada (por exemplo, a chamada para algo que chama extremamente my-func) dentro de a condition-case. Novamente, é isso que é feito na maioria das vezes, pelo menos com a mesma frequência que o uso de catch+ throw. Tudo depende de qual comportamento você deseja.

Desenhou
fonte
Obrigado pela resposta Drew, eu concordo que este é um método bastante comum. No entanto, apenas fazer um retorno antecipado em muitos outros idiomas não implica a complexidade de ter que lidar com um erro. Ao pesquisar o conjunto de perguntas / respostas. Eu estava especificamente procurando alternativas para o estilo "errar", que sempre me parece desajeitado. Eu não especifiquei isso explicitamente no texto da pergunta.
Ocodo 01/02
1
Tudo depende do que você quer fazer. Se você deseja terminar imediatamente, sem processamento / manuseio adicionais, gerar um erro é um bom caminho para uma saída não-local, no Emacs. Se você quer fazer alguma coisa durante uma saída não-local, ou seja, lidar com isso de alguma forma, em seguida catch, unwind-protect, condition-casee similares são úteis. Há uma seção inteira do manual Elisp dedicada a saídas não locais . (E não há nada particularmente kludgy sobre qualquer um deles, IMO.)
de Drew
"Sente" é inteiramente subjetivo, é claro. Obrigado pelo manual não local ref.
Ocodo 01/02