„I” vs „kiedy” dla warunków warunkowych

13

Jest to kontynuacja komentarzy do tej odpowiedzi . Następujące fragmenty kodu wydają się równoważne:

(and a b)

(when a b)

Oczywiście andpozwala stawiać więcej warunków: (and a b c d)oznacza(when (and a b c) d)

Zwykle używam whentylko do wyrażania rozgałęzień. Czy istnieją rzeczywiste różnice? Czy lepiej jest używać jednego lub drugiego?

Nie mam pod ręką źródła Emacsa C; andjest funkcją C; whento makro, które rozwija się do siebie if, co samo w sobie jest funkcją C.

Łaskawy
źródło
„Oczywiście i pozwala” powinno być „Oczywiście„ i „pozwala”, ale to tylko zmiana 2 znaków i nie jest dozwolona edycja. Nie ma przesłonięcia.
Harvey,

Odpowiedzi:

18

TL; DR: when dotyczy efektów ubocznych, andsłuży wyłącznie do wyrażeń boolowskich.

Jak zauważyłeś andi whenróżnią się tylko składnią, ale poza tym są całkowicie równoważne.

Różnica składniowa jest jednak dość ważna: whenobejmuje niejawne podejście prognwokół wszystkich form argumentów oprócz pierwszego. prognjest z natury imperatywną cechą: ocenia wszystkie formy ciała oprócz ostatniej pod kątem ich skutków ubocznych, odrzucając jakąkolwiek wartość, którą zwrócili.

Jako taka whenjest również formą imperatywną: jej głównym celem jest owijanie form wywołujących skutki uboczne, ponieważ tylko wartość ostatniej formy ma znaczenie dla ciała.

andz drugiej strony jest funkcją czystą, której głównym celem jest przyjrzenie się wartościom zwrotnym podanych form argumentów: chyba że jawnie obejdziesz prognktórykolwiek z jego argumentów, wartość każdej formy argumentu jest ważna i żadna wartość nigdy nie jest ignorowana .

Stąd prawdziwa różnica między stylistyczną andi whenstylistyczną: używasz anddo wyrażeń czysto boolowskich i whenstrzeżesz form ubocznych.

Dlatego są to zły styl:

;; `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)))

A te są dobre:

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

Wiem, że niektórzy nie zgadzają się z tym i chętnie używają anddo ochrony przed efektami ubocznymi, ale myślę, że to naprawdę zły styl. Mamy różne formy z jednego powodu: Składnia ma znaczenie . Gdyby tak nie było, wszyscy użylibyśmy tylko iftej jedynej warunkowej formy, której naprawdę potrzebujesz semantycznie w Emacs Lisp. Wszystkie inne formy logiczne i warunkowe można zapisać w kategoriach if.

księżycowy
źródło
2
IMO (tj. W stylu, którego używam) anddotyczy dbania o wartość zwracaną . Niekoniecznie chodzi o stosowanie efektów ubocznych. Jeśli mój program dba o wartość zwracaną, używam and. Jeśli nie, to używam when. IOW, używam when tylko do efektów ubocznych, ale równie dobrze mogę użyć prognjednego z argumentów and. Chodzi o to, czy wartość zwrotu ma znaczenie, a nie o to, czy ma ona znaczenie sama .
Drew
5
andnie jest funkcją w żadnym znanym mi Lisp. W Emacs Lisp jest to specjalna forma, w Common Lisp jest to makro, po prostu dlatego, że oczekuje się zachowania zwarciowego (niemożliwe do osiągnięcia za pomocą funkcji, ponieważ zawsze oceniają swoje argumenty).
Mark Karpov
2
@lunaryorn, Tak, to nie jest tak naprawdę istotne, ale to prawda, więc myślę, że niepoprawne jest pisanie, że andjest funkcją, jeśli nie jest. Nie ma znaczenia, jak istotny jest fakt, że pytanie powinno być zawsze prawidłowe. To wszystko.
Mark Karpov
2
Cóż, nie sądzę, aby „wartość każdej formy argumentu była ważna” jest zgodna z tym, co ta interpretacja. Można by to lepiej wyrazić mówiąc, że żadna forma nie jest oceniana tylko po to, aby jej wartość została odrzucona.
Harald Hanche-Olsen
3
Nieco OT, ale: Różnica jest bardziej niż stylistyczna w Lisps, która nie niloznacza wszystkich „fałsz”, „pustej listy”, „pustki” itp. Na przykład w Scheme and Racket nieprawdziwy wynik whenjest void(tzn. „bez znaczenia”), podczas andgdy jest #f(„fałsz”). Chociaż dla Elisp jest to nie dotyczy, jest to coś, co może rozważyć generalista Lisp.
Greg Hendershott
4

Zacznę od stwierdzenia, że (and a b)i (when a b)na swoim przykładzie zrobić to samo: po pierwsze ajest oceniany. bjest oceniane, jeśli ajest prawdą # .

Ale and i whensą używane do różnych rzeczy.

  • Użyłbyś (and a b)do zwrócenia true #, jeśli BOTH ai btrue # (lub non-zero); i nilinaczej.

  • Użyłbyś (when a b)lub żeby być bardziej poprawnym,

    (when a
       ;; do something like b
       )

    kiedy chcesz, aby kod „b” był wykonywany wtedy i tylko wtedy, gdy ajest to prawda # .


Oto przykład, w którym używasz wheni andrazem:

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

Powyżej funkcja do-cjest wywoływana TYLKO, jeśli OBA ai bprawdziwe # .


Referencje do badań

# Wszystkie odniesienia do prawdy odnoszą się do logicznej wartości PRAWDA.

Kaushal Modi
źródło
3
andnie zwraca, tjeśli wszystkie jego argumenty są inne niż zero, ale wartość ostatniego argumentu.
Sensowna nazwa użytkownika
1
Przez tmiałem na myśli wartość logiczną PRAWDA lub brak wartości zero. Dzięki, wyjaśnię to w mojej odpowiedzi.
Kaushal Modi
Nie sądzę, aby twoja odpowiedź poszła znacznie dalej niż to, co już powiedziałem w pytaniu; Wiem, co robią obie, a ostatni przykład, który podajesz, pojawia się już w moim pytaniu ( (when (and a b) c)). Ponadto część dotycząca tego, w jaki sposób andi whensugeruje, że tak naprawdę są one ogólnie używane, ale sama podstawa tego pytania polegała na tym, że często widzę, że są one używane inaczej (patrz na przykład odpowiedź, do której podałem pytanie).
Clément
Z czysto funkcjonalnego punktu widzenia, kiedy jest przeznaczony do nie kończących się ocen i dotyczy bezpośrednich symboli i logiki logicznej. Programowanie funkcjonalne wymaga tego rozróżnienia; programowanie imperatywne to zepsuć.
Użytkownik Emacsa,
1
Nie sądzę, by „boolean true” było bardziej jasne niż tylko „true”.
YoungFrog,