“And” vs “when” per i condizionali


13

Questo è un seguito ai commenti su questa risposta . I seguenti bit di codice sembrano equivalenti:

(and a b)

(when a b)

Ovviamente andti permette di mettere più condizioni: (and a b c d)significa(when (and a b c) d)

Tendo a usare whensolo per esprimere la ramificazione. Ci sono differenze effettive? È meglio usare l'uno o l'altro?

Non ho la fonte C di Emacs a portata di mano; andè una funzione C; whenè una macro che si espande if, che è essa stessa una funzione C.


"Certamente e lascia" dovrebbe essere "Certamente" e "lascia", ma questo è solo un cambio di 2 caratteri e non è una modifica consentita. Non c'è override.
Harvey,

Risposte:


17

TL; DR: when riguarda gli effetti collaterali, andè per espressioni booleane pure.

Come avete notato, ande whendifferiscono solo nella sintassi, ma sono comunque del tutto equivalente.

La differenza sintattica è piuttosto importante, però: whenavvolge un implicito prognattorno a tutte le forme tranne il primo argomento. prognè una caratteristica intrinsecamente imperativa: valuta tutto tranne l'ultima forma del corpo solo per i loro effetti collaterali, scartando qualsiasi valore restituito.

Come tale, whenè anche una forma imperativa: il suo scopo principale è avvolgere le forme con effetti collaterali, perché solo il valore dell'ultima forma conta davvero per il corpo.

andd'altra parte è una funzione pura, il cui scopo principale è quello di esaminare i valori di ritorno delle forme di argomento fornite: A meno che non si avvolga esplicitamente prognuno qualsiasi dei suoi argomenti, il valore di ogni forma di argomento è importante e nessun valore viene mai ignorato .

Quindi, la vera differenza tra anded whenè stilistica: usi andper le espressioni booleane pure e whenper proteggere le forme con effetti collaterali.

Quindi, questi sono cattivi stili:

;; `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 questi sono buoni:

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

So che alcune persone non sono d'accordo su questo, e felicemente usano andper proteggere gli effetti collaterali, ma penso che questo sia davvero uno stile cattivo. Abbiamo queste diverse forme per un motivo: la sintassi conta . In caso contrario, useremmo sempre e solo if, che è l'unica forma condizionale di cui hai davvero bisogno in Emacs Lisp semanticamente. Tutte le altre forme booleane e condizionali possono essere scritte in termini di if.


2
L'IMO (ovvero, nello stile che uso), andriguarda la cura del valore di ritorno . Non si tratta necessariamente di usare effetti collaterali. Se il mio programma si preoccupa del valore di ritorno, allora lo uso and. In caso contrario, utilizzo when. IOW, lo uso when solo per gli effetti collaterali, ma potrei usare a prognin uno degli argomenti and. Riguarda se il valore di ritorno è importante, non se è importante solo .
Estratto il

5
andnon è una funzione in nessun Lisp di cui sia a conoscenza. In Emacs Lisp è una forma speciale, in Common Lisp è una macro, semplicemente perché si prevede che abbia un comportamento in corto circuito (impossibile da ottenere con le funzioni, perché valutano sempre i loro argomenti).
Mark Karpov,

2
@lunaryorn, Sì, non è davvero rilevante, ma è vero, quindi penso che sia errato scrivere che andè una funzione, quando non lo è. Non importa quanto sia rilevante il fatto per la domanda, dovremmo sempre usare termini corretti, tutto qui.
Mark Karpov,

2
Bene, non penso che "il valore di ogni forma di argomento sia importante" sia coerente con ciò che tale interpretazione. Avrebbe potuto essere meglio espresso dicendo che nessuna forma viene valutata solo per avere il suo valore gettato via.
Harald Hanche-Olsen,

3
Un po 'OT, ma: la differenza è più che stilistica in Lisps che non puniscono nilper significare "falso", "elenco vuoto", "vuoto", ecc. Ad esempio in Scheme e Racket il risultato non veritiero di whenè void(cioè "nessun significato") mentre per andesso è #f("falso"). Anche se questo è N / A per Elisp, è qualcosa che un generalista di Lisp potrebbe prendere in considerazione.
Greg Hendershott,

4

Vorrei iniziare dicendo questo (and a b)e (when a b)nel tuo esempio fai la stessa cosa: il primo aè valutato. bviene valutato se aè true # .

Ma and e whensono usati per cose diverse.

  • Utilizzerai (and a b)per restituire true # se ENTRAMBI ae bsono true # (o non nulli); e nilaltrimenti.

  • Utilizzeresti (when a b)o per essere più corretto,

    (when a
       ;; do something like b
       )

    quando si desidera eseguire il codice "b" se e solo se aè true # .


Ecco un esempio in cui usi whene andinsieme:

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

Sopra, la funzione do-cè chiamata SOLO se ENTRAMBI ae bsono veri # .


Riferimenti per studio

# Tutti i riferimenti a true si riferiscono a VERO booleano.


3
andnon restituisce tse tutti i suoi argomenti sono diversi da zero, ma il valore dell'ultimo argomento.
Nome utente significativo

1
Con t, intendevo il VERO booleano o non nullo. Grazie, lo chiarirò nella mia risposta.
Kaushal Modi,

Non credo che la tua risposta vada molto oltre ciò che ho già affermato nella domanda; So cosa fanno entrambi, e l'ultimo esempio che dai appare già nella mia domanda ( (when (and a b) c)). Inoltre, la parte su come ande whensuggerisce che è effettivamente così che vengono utilizzati in generale, ma la base stessa per questa domanda era il fatto che spesso li vedo usati in modo diverso (vedi ad esempio la risposta che ho collegato alla mia domanda).
Clément,

Dal punto di vista di un costrutto puramente funzionale, quando è per valutazioni non terminanti ed è per simboli diretti e logica booleana. La programmazione funzionale ha bisogno di questa distinzione; la programmazione imperativa lo elimina.
Utente Emacs

1
Non credo che il "vero booleano" sia più chiaro del semplice "vero".
YoungFrog,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.