Khi nào trích dẫn một biểu thức lambda?


30

H: Khi nào, nếu có, có ích khi trích dẫn a lambda, và khi nào, nếu có, chúng ta không được trích dẫn a lambda?

Mọi người sử dụng lambdas theo ba cách:

  1. trơn: (lambda (x) x)
  2. trích dẫn: '(lambda (x) x)
  3. trích dẫn sắc nét: #'(lambda (x) x)

Chủ đề SO này thảo luận về ba loại, chủ đề SO này giải thích tại sao không trích dẫn (NB: không sắc nét ) lambdachủ đề SO này cũng thảo luận về sự khác biệt giữa trích dẫn và trích dẫn sắc nét.

Bây giờ, nút thủ công trên các hàm ẩn danh và chuỗi doc để lambdalưu ý rằng đó lambdalà tự trích dẫn:

Một cuộc gọi của hình thức (lambda ARGS DOCSTRING INTERACTIVE BODY)là tự trích dẫn; kết quả của việc đánh giá biểu thức lambda là chính biểu thức đó. Biểu thức lambda sau đó có thể được coi là một hàm ...

Vì vậy, nó xuất hiện (lambda (x) x)#'(lambda (x) x)tương đương, nhưng '(lambda (x) x)không (quan trọng nhất là khi biên dịch byte).

Có vẻ như người ta hiếm khi muốn trích dẫn một lambda, nhưng nó không rõ ràng với tôi khi nào, nếu có, chúng ta nên, hoặc không nên, trích dẫn sắc nét:

  • Là trích dẫn sắc nét lambdachỉ đơn giản là một sự lựa chọn phong cách, hoặc có những trường hợp trong đó trích dẫn sắc nét là thực sự hữu ích?
  • Có những trường hợp trong đó chúng ta không được trích dẫn a lambda, nghĩa là, khi làm như vậy sẽ làm thay đổi ý nghĩa của mã?

Câu trả lời:


28

Ngày xưa, câu nói sắc sảo là cần thiết cho lambdas, bây giờ điều đó không còn nữa.

Vì vậy, có vẻ như (lambda (x) x) và # '(lambda (x) x) là tương đương, nhưng' (lambda (x) x) không (quan trọng nhất là khi biên dịch byte).

Vâng. Trong thực tế, hai cái đầu hoàn toàn giống nhau khi được đánh giá. Như được mô tả trong trang hướng dẫn bạn đã liên kết:

Các hình thức sau đây đều tương đương:

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

Ngoài việc cố gắng hỗ trợ các phiên bản Emacs từ hai thập kỷ trước, không bao giờ có lý do để trích dẫn một lambda.

Vì vậy, không.


Như một sidenote:

  • Hard trích dẫn một lambda (với ') không tạo ra sự khác biệt, nó ngăn chặn việc biên dịch byte. Tôi không thể nghĩ ra một kịch bản nào hữu ích, nhưng ai biết được.

  • Backtic là trích dẫn duy nhất thực sự hữu ích với lambdas, nhưng chỉ khi bạn không sử dụng ràng buộc từ vựng vì một số lý do.


Xem xét thêm một liên kết đến phần Chức năng ẩn danh của hướng dẫn sử dụng có chứa một ví dụ giải thích hiệu quả của việc trích dẫn trên biên dịch byte.
Constantine

@Constantine Xong. Tôi đã lười biếng vì tôi đang dùng điện thoại và OP dù sao cũng đã liên kết nó.
Malabarba

Bạn có thể làm rõ những gì bạn có nghĩa là không sử dụng ràng buộc từ vựng và backtick? Cảm ơn.
coredump

@coredump Với liên kết động, cách duy nhất để làm cho các biến bên ngoài có thể truy cập được bên trong lambda là xây dựng lambda theo cách thủ công như một danh sách với biến bên trong. Backtics là tốt cho loại điều đó.
Malabarba

BTW, tôi không nghĩ "ngày xửa ngày xưa" thực sự áp dụng: khi tôi điều tra chủ đề này trong lịch sử sửa đổi, tôi thấy rằng nó lambdađã được định nghĩa là một macro bổ sung functiontrở lại như tôi có thể đi. IOW nếu #'đã được cần thiết tại một số điểm, đó là trong mã phát triển rất sớm. Nó chắc chắn là không cần thiết trong Emacs-18.
Stefan

5

lambdakhông có ý nghĩa gì khi nó không được trích dẫn, các phiên bản gần đây của Emacs Lisp đi theo Lisp chung (ANSI) trong việc diễn giải không được trích dẫn (lambda...)#'(lambda...). Hai ký hiệu gần như chính xác tương đương (ngoại trừ trong cấu trúc được trích dẫn).

Cho dù thích (lambda...)hay #'(lambda...)do đó hoàn toàn là một vấn đề của phong cách. Một số người thích hình thức trần trụi, tránh tiếng ồn cú pháp, trong khi những người khác (bao gồm cả tôi) thích hình thức trích dẫn.


Điều này mâu thuẫn với hướng dẫn elisp: "Trong Emacs Lisp, một danh sách như vậy là một biểu thức hợp lệ để đánh giá một đối tượng hàm."
djechlin 30/03/2015

8
"Các phiên bản gần đây" như trong "các phiên bản được phát hành sau năm 1990 hoặc lâu hơn" ;-)
Stefan

0

Thêm một chút lịch sử bổ sung, trên tài khoản xem Di sản lịch sử # '(lambda ...)?

https://debbugs.gnu.org/cgi/ormsreport.cgi?orms=4290 gợi ý rằng:

Bắt đầu với Emacs 22, một lambdabiểu mẫu được biên dịch byte khi nó được sử dụng như một hàm, bất kể nó có trước functionhay không #'. Với các phiên bản Emacs trước 22, bạn phải sử dụng rõ ràng #' hoặc functionnếu bạn muốn biểu mẫu được biên dịch byte.

Tôi không biết về trình biên dịch byte, nhưng tôi có thể thấy rằng ít nhất là từ năm 1993, lambdamacro đã trả về một (function (lambda ...))biểu mẫu.

https://www.iro.umontreal.ca/~monnier/hopl-4-emacs-lisp.pdf cũng nói:

Thật thú vị (không giống như MacLisp), lambdavề mặt kỹ thuật không phải là một phần của ngôn ngữ Elisp cho đến khoảng năm 1991 khi nó được thêm vào dưới dạng vĩ mô, ngay từ đầu trong quá trình phát triển Emacs-19. Trong Emacs-18, các hàm ẩn danh được viết dưới dạng các giá trị được trích dẫn của biểu mẫu:

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

Mặc dù lambdamacro đã khiến trích dẫn này không cần thiết trong gần 30 năm nay, nhiều trường hợp thực hành này vẫn xảy ra trong mã Elisp, mặc dù nó ngăn chặn quá trình biên dịch byte của cơ thể. Một phần nào đó có liên quan, chỉ trong năm 1993, Lucid Emacs 19.8 đã nhập khẩu tốc #'...ký của người đọc (function ...)từ MacLisp. Emacs theo sau phù hợp năm sau.


0

Chỉ muốn đưa ra một ví dụ thực tế về việc sử dụng biểu thức lambda ngược. Đó là về bóng tối liên kết / biến từ vựng, sử dụng biểu thức lambda ngược và các biến tham chiếu bằng dấu phẩy giúp có thể nhận được giá trị toàn cầu của chúng.

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

M-x [RET] eval-buffer đầu ra "Cũ ngẫu nhiên"

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

M-x [RET] eval-buffer đầu ra "Ngẫu nhiên"

Một ví dụ thứ ba kết hợp biến toàn cục và biến cục bộ của biến

;; -*- 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 đầu ra "Ngẫu nhiên" "Cũ ngẫu nhiên"


@npostavs không phải là điểm chính với ví dụ của tôi nhưng tôi đã sửa đổi ví dụ của mình để tránh thực hành xấu đó
cjohansson

Tốt hơn, mặc dù tôi vẫn không tin rằng đây là một cải tiến so với việc chỉ chọn một tên khác cho ràng buộc bên trong.
npostavs
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.