"E" vs "quando" para condicionais

13

Este é um acompanhamento dos comentários sobre esta resposta . Os seguintes bits de código parecem ser equivalentes:

(and a b)

(when a b)

É claro que andpermite colocar mais condições: (and a b c d)significa(when (and a b c) d)

Eu costumo usar whenapenas para expressar ramificação. Existem diferenças reais? É melhor usar um ou outro?

Eu não tenho a fonte C do Emacs em mãos; andé uma função C; whené uma macro que se expande para if, que por si só é uma função C.

Clemente
fonte
"Claro e vamos" deve ser "Claro` e `vamos", mas isso é apenas uma alteração de 2 caracteres e não é uma edição permitida. Não há substituição.
Harvey

Respostas:

17

TL; DR: when trata de efeitos colaterais, andé para expressões booleanas puras.

Como você notou, ande whendiferem apenas na sintaxe, mas são inteiramente equivalentes.

A diferença sintática é bastante importante: whenenvolve um implícito em progntorno de todas as formas, exceto o primeiro argumento. progné uma característica inerentemente imperativa: avalia apenas a última forma do corpo apenas para seus efeitos colaterais, descartando qualquer valor que eles retornassem.

Como tal, também whené uma forma imperativa: seu principal objetivo é envolver as formas de efeito colateral, porque apenas o valor da última forma realmente importa para o corpo.

andpor outro lado, é uma função pura, cujo objetivo principal é examinar os valores de retorno dos formulários de argumento fornecidos: a menos que você explique explicitamente prognqualquer um de seus argumentos, o valor de cada formulário de argumento é importante e nenhum valor é ignorado. .

Portanto, a verdadeira diferença entre ande whené estilística: você usa andpara expressões booleanas puras e whenprotege as formas de efeito colateral.

Portanto, estes são um estilo ruim:

;; `when' used for a pure boolean expression
(let ((use-buffer (when (buffer-live-p buffer)
                    (file-exists-p (buffer-file-name buffer)))))
  ...)

;; `and' used as guard around a side-effecting form
(and (buffer-file-name buffer) (write-region nil nil (buffer-file-name buffer)))

E estes são bons:

(let ((use-buffer (and (buffer-live-p buffer)
                       (file-exists-p (buffer-file-name buffer)))))
  ...)

(when (buffer-file-name buffer)
 (write-region nil nil (buffer-file-name buffer)))

Eu sei que algumas pessoas discordam disso e usam alegremente andpara proteger os efeitos colaterais, mas acho que esse é um estilo muito ruim. Temos essas formas diferentes por um motivo: a sintaxe é importante . Caso contrário, todos nós apenas usaríamos if, que é a única forma condicional que você realmente precisa no Emacs Lisp semântica. Todas as outras formas booleanas e condicionais podem ser escritas em termos de if.

lunaryorn
fonte
2
OMI (ou seja, no estilo que eu uso), andtrata-se de se preocupar com o valor de retorno . Não se trata necessariamente de usar efeitos colaterais. Se meu programa se importa com o valor de retorno, então eu uso and. Se não, então eu uso when. IOW, eu uso when apenas para efeitos colaterais, mas posso usar a prognem um dos argumentos para and. É sobre se o valor de retorno é importante, não se é o único que importa.
Tirou
5
andnão é uma função em nenhum Lisp que eu conheça. No Emacs Lisp, é uma forma especial; no Common Lisp, é uma macro, simplesmente porque é esperado que ele tenha um comportamento de curto-circuito (impossível de obter com funções, porque eles sempre avaliam seus argumentos).
22615 Mark Karpov
2
@ Lunaryorn, Sim, não é realmente relevante, mas é verdade, então eu acho incorreto escrever que andé uma função, quando não é. Não importa o quão relevante seja o fato para a pergunta, devemos sempre usar termos corretos, só isso.
Mark Karpov
2
Bem, eu não acho que "o valor de toda forma de argumento seja importante" seja consistente com o que essa interpretação. Poderia ter sido melhor expresso dizendo que nenhuma forma é avaliada apenas para ter seu valor jogado fora.
Harald Hanche-Olsen
3
Um pouco AT, mas: A diferença é mais do que estilística nos Lisps que não trocam nilde significado como "falso", "a lista vazia", ​​"nula" etc. etc. Por exemplo, em Esquema e raquete, o resultado não-verdadeiro de whenis void(ie "sem sentido") enquanto que para ( andis #f"falso"). Embora seja N / A para Elisp, é algo que um generalista do Lisp pode considerar.
Greg Hendershott
4

Deixe-me começar dizendo isso (and a b)e, (when a b)no seu exemplo, faça o mesmo: Primeiro aé avaliado. bé avaliado se afor verdadeiro # .

Mas and e whensão usados ​​para coisas diferentes.

  • Você usaria (and a b)para retornar # verdadeiro se AMBOS e forem # verdadeiros (ou não nulos); e caso contrário.abnil

  • Você usaria (when a b)ou, para ser mais correto,

    (when a
       ;; do something like b
       )

    quando você deseja que o código "b" seja executado se e somente se afor verdadeiro # .


Aqui está um exemplo em que você usa whene andjuntos:

(when (and a b)
   (do-c))

Acima, a função do-cé chamada SOMENTE se AMBOS ae bfor verdadeira # .


Referências para estudo

# Todas as referências a true referem-se ao Boolean TRUE.

Kaushal Modi
fonte
3
andnão retornará tse todos os seus argumentos forem nulos, mas o valor do último argumento.
Nome de usuário significativo
1
Por t, eu quis dizer o booleano VERDADEIRO ou não nulo. Obrigado, vou esclarecer isso na minha resposta.
Kaushal Modi
Não acho que sua resposta vá muito além do que já afirmei na pergunta; Eu sei o que os dois fazem, e o exemplo final que você dá já aparece na minha pergunta ( (when (and a b) c)). Além disso, a parte sobre como ande whensugere que é realmente assim que eles são usados ​​em geral, mas a própria base para essa pergunta foi o fato de eu frequentemente os ver usados ​​de maneira diferente (veja, por exemplo, a resposta à qual vinculei na minha pergunta).
Clément
Do ponto de vista da construção puramente funcional, quando é para avaliações sem finalização e e é para símbolos diretos e lógica booleana. A programação funcional precisa dessa distinção; a programação imperativa elimina isso.
Emacs usuário
1
Eu não acho que "boolean true" seja mais claro do que apenas "true".
YoungFrog