Kiedy / dlaczego powinienem używać prognozy?

19

Widziałem, że jestem często prognużywany podczas przeglądania plików konfiguracyjnych doświadczonych użytkowników Emacsa. Znalazłem to miłe wytłumaczenieprogn , ale naprawdę jestem ciekawy, jaka jest korzyść z używania tej funkcji? Weźmy na przykład ten fragment kodu (wzięty z konfiguracji Sacha Chua ):

(use-package undo-tree
  :defer t
  :ensure t
  :diminish undo-tree-mode
  :config
  (progn
    (global-undo-tree-mode)
    (setq undo-tree-visualizer-timestamps t)
    (setq undo-tree-visualizer-diff t)))

Czy jest jakaś istotna różnica między powyższą konfiguracją a tym?

(use-package undo-tree
  :defer t
  :ensure t
  :diminish undo-tree-mode
  :config
  (global-undo-tree-mode)
  (setq undo-tree-visualizer-timestamps t)
  (setq undo-tree-visualizer-diff t))

Wydaje mi się, że pierwszy przykład jest w jakiś sposób bardziej przejrzysty, mimo że ma więcej składni, a moją intuicją jest to, że może zaistnieć pewien wzrost wydajności progn, ale nie jestem pewien. Dziękujemy za wszelkie spostrzeżenia!

Elethan
źródło
7
W tym szczególnym przypadku nie ma różnicy: use-packagezawinie prognwokół twoich: config formularzy, jeśli go brakuje. Wypróbuj: możesz umieścić punkt na końcu a (use-package ...)i wywołać, M-x pp-macroexpand-last-sexpaby zobaczyć, jak makro jest rozwijane. Zobaczysz, że jest identyczny dla tych dwóch przykładów.
glucas
Przykład gdzie prognjest potrzebny: emacs.stackexchange.com/questions/39172/…
npostavs

Odpowiedzi:

11

prognjest zwykle używany w przypadku makr. Niektóre makra ( use-packageto makro, ostatnio sprawdzone) akceptują tylko 1 formularz , podczas gdy inne używają wszystkich pozostałych formularzy .

progn jest używany w pierwszym przypadku do przekształcenia sekwencji formularzy w jedną formę.

W twoich przykładach pierwszy używa, progna zatem jest 1 forma po :config. W drugim są 3 formy . Jeśli use-packagemakro oczekuje tylko 1 formularza :config, spowoduje błąd.

Warto zauważyć, że użycie progndziała w obu przypadkach, pomijając to działa tylko wtedy, gdy makro akceptuje wiele formularzy. W rezultacie niektóre osoby wolą po prostu zawsze używać progn, ponieważ zawsze będzie działać.

J David Smith
źródło
15
To naprawdę nie ma nic wspólnego z makrami. Niektóre funkcje i makra mają tak zwane „niejawne progn”, co oznacza, że ​​akceptują dowolną liczbę sexps jako osobne argumenty i oceniają je kolejno. Inni nie, a zamiast tego oczekują / dopuszczają tylko jeden argument, w którym możesz chcieć oceniać wiele sexps po kolei. To wszystko progn: pozwala ci podać pojedynczy sexp, który po ocenie ocenia prognsekwencję w sekwencjach. Porównaj (if true (progn a b c))z (when true a b c). W obu przypadkach a, bi csą oceniane w kolejności.
Drew
4
Nie zgadzam się, że 99% przypadków użycia dotyczy makr itp. Każdą funkcję lub makro można przekazać prognseksu, który zawiera wiele seksu, które są oceniane pod kątem ich skutków ubocznych, przed zwróceniem wyniku ostatniego z nich. To naprawdę nie ma nic wspólnego z makrami. Makra „jako przykład” są w porządku. Sprawianie wrażenia, które prognistnieje w przypadku makr i że 99% jego użycia dotyczy makr, jest mylące, IMO. prognpolega na przerzuceniu sekwencji ocen na jedną płeć (aby dopasować się do danego oczekiwanego argumentu), a zatem na efektach ubocznych .
Drew
5
1. prognzostał wprowadzony w Lisp 1.5 (patrz sekcja Funkcja programu ) w 1962 roku, na długo przed dodaniem makr do Lisp. Celem jest sekwencyjna ocena wyrażeń pod kątem ich skutków ubocznych. 2. Zobacz, prognjak sam Emacs przedstawia prognsię programistom Elisp. Zobacz zap-to-charkod prostego przypadku użycia.
Drew
2
Możemy zgodzić się nie zgadzać. Chodzi mi o to, że nieprogn ma to nic wspólnego z makrami. Jego celem jest oczywiście „ przekazywanie wielu formularzy jako pojedynczej formy ” (twoich słów) - czy to do funkcji, makra czy specjalnego formularza, ale także do „ Eval BODY formuje się sekwencyjnie i zwraca wartość ostatniego ” (ciąg znaków doc ). Teraz jak zawsze (rzeczywiście „te dni”!). Oczywiście, uporządkowana ocena jest ważna tylko dla skutków ubocznych. Dany przypadek użycia (makro lub nie) może, ale nie musi mieć znaczenia kolejność oceny, ale to nie ma znaczenia. Emacs Lisp nie jest pod tym względem wyjątkowy.
Drew
10

Najważniejszy powód prognozy opisano w pierwszym wierszu dokumentacji prognozy ( wyróżnienie dodane):

progn jest specjalną formą, która powoduje, że każdy z jej argumentów jest analizowany po kolei, a następnie zwraca wartość ostatniego.

Uzupełnienie:

Bez prognozy sekwencja nie jest gwarantowana, szczególnie jeśli kolejne wyrażenia zależą od efektów ubocznych lub zwracanych wartości poprzednich wyrażeń. progn wymusza sekwencję wykonywania taką samą jak sekwencja tekstowa. Pomaga nie mylić wykonania z analizą. To zachowanie sięga podstaw struktur kontrolnych seplenienia i programowania funkcjonalnego. Oto fragment (podkreślenie dodane) z podręcznika Lisp :

Wbudowane struktury kontrolne są formami specjalnymi, ponieważ ich podformularze niekoniecznie są oceniane lub nie są oceniane kolejno .

Czy prognostyka poprawia wydajność?

Analiza składni, nie. Wykonanie, nr W najlepszym razie może się równać, ale nigdy nie magicznie zwiększyć wydajności.

Po progn wykorzystywane ?

... najczęściej wewnątrz odprężenia i, lub , lub w ówczesnej części if .

Użytkownik Emacsa
źródło
Nie jestem pewien, dlaczego to takie ważne. Emacs zawsze ocenia sekwencyjnie.
lunaryorn,
2
@lunaryorn zobacz zaktualizowaną odpowiedź.
Użytkownik Emacsa,
Nie jestem pewien, czy rozumiem twoją edycję. Oczywiście istnieją specjalne formularze z inną kolejnością oceny (np. if), Ale normalna kolejność oceny (np. Formularze najwyższego poziomu, ciała funkcji itp.) Jest tekstowa. Oczywiście, do pewnego stopnia jest to domniemane progn, ale zwykle nie jest to do czego używasz progn w swoim własnym kodzie.
lunaryorn,
Myślę, że ręczny węzeł Elisp, (elisp) Sequencingktóry łączysz, mówi wszystko.
Basil
10

Lepszym sposobem zrozumienia tego, co prognjest, jest porównanie go z rodziną: prog1i prog2. nLub 1lub 2część nazwy oznacza rachunku z listy, której wynikiem są Państwo zainteresowani. Innymi słowy, prognzwróci wynik ostatniego zestawienia w nim zawarte, natomiast prog1powróci pierwsza, a podobna prog2.

Dziś ta funkcjonalność wydaje się nieco niewygodna, ponieważ uczymy się, że albo oczekujemy, że program zwróci się w ostatniej instrukcji, albo wyraźnie instruujemy, co zwrócić. W ten sposób prog1i prog2są bardzo rzadko stosowane. Jeśli jednak o tym pomyślisz, ma to sens, a oto jak:

Różne języki programowania używają różnych strategii do opisania swojej semantyki. Rodzina Lisp była ściśle związana z semantyką denotacyjną. Bez wchodzenia w szczegóły, tego rodzaju semantyka ma szczególne trudności z czymś, co znamy jako „stwierdzenia”, które nie są „wyrażeniami”. Znaczenia kodu są zwykle rozpatrywane w kategoriach kombinacji funkcji, podczas gdy „instrukcja”, której nie można nawet opisać jako funkcji (ponieważ niczego nie ocenia) jest trudna do opanowania. Ponieważ jednak Lisp dopuszcza skutki uboczne, czasami programista chciałby użyć wyrażenia bez używania jego wartości w taki sposób, aby nie wpływał na wyrażenie następujące po nim. I tu właśnie progXwchodzi rodzina.

W językach podobnych do C funkcja ta jest czasami znana jako punkt sekwencji (tj. ;I ,na przykład). prognjest tak samo niezbędny dla języków podobnych do Lisp, jak i dla języków podobnych ;do C. Jest to podstawowa cecha języka, którego nie można łatwo zastąpić. Jednak w przeciwieństwie do języków w stylu C, Lisp ma tendencję do budowania abstrakcji syntaktycznych, całkowicie ukrywając składnię niższego poziomu. Być może dlatego nie prognużywasz go tak często, ale jest to jeden z ważnych elementów budujących abstrakcje języka wyższego poziomu (makra).

wvxvw
źródło