Kiedy zacytować wyrażenie lambda?

30

P: Kiedy, jeśli w ogóle, użyteczne jest precyzyjne zacytowanie a lambda, a kiedy, jeśli w ogóle, nie powinniśmy ostro zacytować a lambda?

Ludzie używają lambdas na trzy sposoby:

  1. Równina: (lambda (x) x)
  2. zacytowany: '(lambda (x) x)
  3. ostre cytaty: #'(lambda (x) x)

Ten wątek SO omawia trzy typy, ten wątek SO wyjaśnia, dlaczego nie cytować (NB: nie ostre-cytowanie ) lambda, a ten wątek SO omawia także różnice między cytowaniem a ostrym cytowaniem.

Teraz ręczny węzeł funkcji anonimowych i dokumentacja dla lambdauwagi, że lambdas są cytatami:

Wywołanie formularza (lambda ARGS DOCSTRING INTERACTIVE BODY)jest cytatem własnym; wynikiem oceny wyrażenia lambda jest samo wyrażenie. Wyrażenie lambda można następnie traktować jako funkcję ...

Tak więc wydaje się, że (lambda (x) x)i #'(lambda (x) x)są równoważne, ale '(lambda (x) x)nie jest (co najważniejsze, gdy bajt kompilacji).

Wygląda na to, że rzadko chcą zacytowaćlambda , ale to jest dla mnie jasne, kiedy, jeśli w ogóle, to powinien, czy nie powinien, ostrych cytat:

  • Czy ostre cytowanie jest lambdapo prostu stylistycznym wyborem, czy też są okoliczności, w których ostre cytowanie jest rzeczywiście przydatne?
  • Istnieją okoliczności, w których musimy nie ostrych zacytować lambda, to znaczy, gdy spowodowałoby to zmianę znaczenia kodu?
Dan
źródło

Odpowiedzi:

28

Pewnego razu ostry cytat był konieczny dla lambdów, teraz już tak nie jest.

Wygląda więc na to, że (lambda (x) x) i # '(lambda (x) x) są równoważne, ale' (lambda (x) x) nie jest (co najważniejsze, przy kompilacji bajtów).

Tak. W rzeczywistości pierwsze dwa są całkowicie identyczne, gdy są oceniane. Zgodnie z opisem na podlinkowanej stronie podręcznika :

Następujące formularze są równoważne:

(lambda (x) (* x x)) 
(function (lambda (x) (* x x))) 
#'(lambda (x) (* x x))

Poza próbą wspierania wersji Emacsa sprzed dwóch dekad, nigdy nie ma powodu, aby ostro cytować lambdę.

Więc nie rób tego.


Na marginesie:

  • Trudne cytowanie lambda (z ') robi różnicę, zapobiega kompilacji bajtów. Nie mogę wymyślić scenariusza, w którym jest to przydatne, ale kto wie.

  • Backtic jest jedynym cytatem, który jest naprawdę przydatny w lambdach, ale tylko wtedy, gdy z jakiegoś powodu nie używasz wiązania leksykalnego.

Malabarba
źródło
Rozważ dodanie linku do sekcji Funkcje anonimowe w podręczniku, która zawiera przykład wyjaśniający wpływ cytowania na kompilację bajtów.
Constantine
@Constantine Done. Stałem się leniwy, bo rozmawiam przez telefon, a OP i tak już to zrobił.
Malabarba
Czy możesz wyjaśnić, co masz na myśli mówiąc o tym, że nie używasz leksykalnego wiązania i odwrotu? Dzięki.
Coredump
@coredump W przypadku dynamicznego wiązania jedynym sposobem, aby zmienne zewnętrzne były dostępne w lambda, jest ręczne zbudowanie lambda jako listy ze zmienną w środku. Backtics są odpowiednie do tego rodzaju rzeczy.
Malabarba
BTW, nie wydaje mi się, żeby „dawno temu” tak naprawdę miało zastosowanie: kiedy badałem ten temat w historii zmian, stwierdziłem, że lambdazostało ono zdefiniowane jako makro, które dodaje słowo „ functiontak”, jak tylko mogłem. IOW, jeśli #'był kiedyś potrzebny, był w bardzo wczesnym kodzie programistycznym. Z pewnością nie był już potrzebny w Emacs-18.
Stefan
5

Ponieważ lambdanie ma sensu, gdy nie jest cytowany, najnowsze wersje Emacsa Lispa śledzą (ANSI) Common Lisp w interpretacji nie cytowanej (lambda...)jako #'(lambda...). Te dwa oznaczenia są prawie dokładnie równoważne (z wyjątkiem struktury cytowanej).

Czy preferować, (lambda...)czy #'(lambda...)też jest to wyłącznie kwestia stylu. Niektórzy wolą formę nagą, która unika szumu syntaktycznego, podczas gdy inni (w tym ja) wolą cytowaną formę.

jch
źródło
Jest to sprzeczne z podręcznikiem elisp: „W Emacs Lisp taka lista jest prawidłowym wyrażeniem, które ocenia na obiekt funkcji”.
djechlin
8
„Najnowsze wersje” jak w „wersjach wydanych po 1990 r.” ;-)
Stefan
0

Dodanie odrobiny dodatkowej historii, ze względu na to, że Czy # '(lambda ...) jest dziedzictwem historycznym?

https://debbugs.gnu.org/cgi/bugreport.cgi?bug=4290 sugeruje, że:

Począwszy od Emacsa 22, lambdaforma jest kompilowana bajtowo, gdy jest używana jako funkcja, niezależnie od tego, czy poprzedza ją, functionczy #'. W przypadku wersji Emacsa wcześniejszych niż 22, musisz jawnie użyć #' lub, functionjeśli chcesz, aby formularz był skompilowany bajtowo.

Nie wiem o kompilatorze bajtów, ale widzę, że przynajmniej w 1993 r. lambdaSamo makro zwróciło (function (lambda ...))formularz.

https://www.iro.umontreal.ca/~monnier/hopl-4-emacs-lisp.pdf mówi również:

Co ciekawe (w przeciwieństwie do MacLisp), lambdatechnicznie nie był częścią języka Elisp aż do około 1991 roku, kiedy został dodany jako makro, na początku rozwoju Emacsa-19. W Emacs-18 anonimowe funkcje zostały zapisane jako cytowane wartości formularza:

'(lambda (..ARGS..) ..BODY..)

Chociaż lambdamakro sprawiło, że ten cytat jest niepotrzebny od prawie 30 lat, wiele przypadków tej praktyki wciąż występuje w kodzie Elisp, nawet jeśli zapobiega to kompilacji bajtów ciała. W pewnym sensie tylko w 1993 roku Lucid Emacs 19.8 zaimportował #'...skrót (function ...)od MacLisp. Emacs poszedł w ich ślady rok później.

phils
źródło
0

Po prostu chcę podać praktyczny przykład użycia zwrotnego wyrażenia lambda. Chodzi o powiązanie leksykalne / cieniowanie zmiennych, użycie zwrotnego wyrażenia lambda, a odwołanie do zmiennych przecinkiem umożliwia uzyskanie ich wartości globalnej.

;; -*- lexical-binding:t -*-
(let ((my-variable "Random old"))
  (funcall `(lambda()
             (let ((my-variable "Random"))
               (message ,my-variable)))))

M-x [RET] eval-buffer wyprowadza „Losowo stary”

;; -*- lexical-binding:t -*-
(let ((my-variable "Random old"))
  (funcall (lambda()
             (let ((my-variable "Random"))
               (message my-variable)))))

M-x [RET] eval-buffer wyprowadza „Losowo”

Trzeci przykład łączenia zmiennej globalnej i zmiennej lokalnej zmiennej

;; -*- lexical-binding:t -*-
(let ((my-variable "Random old"))
  (funcall `(lambda()
              (let ((my-variable "Random"))
                (message my-variable)
                (message ,my-variable)))))

M-x [RET] eval-buffer wyprowadza „Random” „Random old”

cjohansson
źródło
@npostavs nie o to chodziło w moim przykładzie, ale zmodyfikowałem mój przykład, aby uniknąć również złej praktyki
cjohansson
Lepiej, choć nadal nie jestem przekonany, że jest to poprawa w stosunku do wyboru innej nazwy dla wewnętrznego wiązania.
npostavs