Các #'
và funcall
ký hiệu là cần thiết trong Common Lisp vì ngôn ngữ này là một cái gọi là "Lisp-2", nơi một biểu tượng cho trước có thể có hai riêng biệt và không liên quan "ý nghĩa" chính thường được liệt kê như
- khi sử dụng như yếu tố đầu tiên của một hình thức nó có nghĩa là một hàm
- khi được sử dụng ở bất kỳ nơi nào khác nó có nghĩa là một biến
đây là những lời giải thích gần đúng, như bạn sẽ thấy trong ví dụ sau đó "el đầu tiên ement của một hình thức "và" bất kỳ nơi nào khác "không phải là định nghĩa chính xác.
Hãy xem xét ví dụ:
mã trên bản in 144
... nó có vẻ ngạc nhiên lúc đầu nhưng lý do là cùng tên square
được sử dụng với hai ý nghĩa khác nhau: chức năng mà cho một đối số trả về kết quả của việc nhân đối số của chính nó và biến số địa phương với giá trị 12.
Cách dùng thứ nhất và thứ ba của tên square
ý nghĩa là hàm tên là square
và tôi đã vẽ tên bằng màu đỏ. Thay vào đó, lần sử dụng thứ hai và thứ tư là khoảng một biến số có tên là square
và được tô màu xanh lam.
Làm thế nào Common Lisp có thể quyết định cái nào? vấn đề là vị trí ...sau defun
rõ ràng trong trường hợp này là tên hàm, giống như tên hàm trong phần đầu tiên của (square square)
. Tương tự như yếu tố đầu tiên của một danh sách bên trong một hình thức let
nó rõ ràng là một tên biến và nó cũng là một tên biến trong phần thứ hai của (square square)
.
Điều này có vẻ khá tâm lý ... phải không? Vâng có thực sự là một số chia tách trong cộng đồng Lisp về nếu ý nghĩa kép này sẽ làm cho mọi thứ đơn giản hoặc phức tạp hơn và đó là một trong những khác biệt chính giữa Common Lisp và Scheme.
Nếu không đi vào chi tiết, tôi sẽ nói rằng sự lựa chọn điên rồ này đã được thực hiện để làm cho các macro Lisp hữu ích hơn, cung cấp đủ vệ sinh để làm cho chúng hoạt động độc đáo mà không cần thêm độ phức tạp và khả năng biểu đạt được xóa của các macro đủ vệ sinh . Chắc chắn đó là một biến chứng khiến khó giải thích ngôn ngữ hơn cho bất cứ ai học nó (và đó là lý do tại sao Scheme được coi là ngôn ngữ tốt hơn (đơn giản hơn) để giảng dạy), nhưng nhiều chuyên gia lispers nghĩ rằng đó là một lựa chọn tốt công cụ tốt hơn cho các vấn đề thực sự. Ngoài ra trong ngôn ngữ của con người ngữ cảnh cũng đóng một vai trò quan trọng và không có vấn đề gì nghiêm trọng đối với con người, đôi khi cùng một từ có thể được sử dụng với các ý nghĩa khác nhau (ví dụ như một danh từ hoặc một động từ như "California là tiểu bang tôi sống trong "hoặc" Nhà nước ý kiến của bạn "). Tuy nhiên, ngay cả trong Lisp-2 bạn cần sử dụng các hàm làm giá trị, ví dụ như chuyển chúng thành tham số hoặc lưu chúng thành cấu trúc dữ liệu, hoặc bạn cần sử dụng các giá trị làm hàm, ví dụ như gọi hàm nhận được như tham số (trường hợp cốt truyện của bạn) hoặc đã được lưu trữ ở đâu đó. Đây là nơi #'
và funcall
đi vào chơi ...
#'foo
thực sự chỉ là một phím tắt cho (function foo)
, giống hệt như 'x
là một phím tắt cho (quote x)
. "Chức năng" điều này là một hình thức đặc biệt mà đặt một cái tên (foo
trong trường hợp này) trả về chức năng liên quan như một giá trị, mà bạn có thể lưu trữ trong các biến hoặc vượt qua xung quanh:
(defvar *fn* #'square)
trong đoạn code trên ví dụ biến *fn*
sẽ nhận hàm được xác định trước đó. Giá trị hàm có thể được điều khiển như bất kỳ giá trị nào khác như chuỗi hoặc số.
funcall
thì ngược lại, cho phép để gọi một chức năng không sử dụng tên gọi của nó nhưng bằng cách sử dụng một giá trị ...
(print (funcall *fn* 12))
các mã trên sẽ hiển thị 144 ... bởi vì các chức năng đã được lưu trữ trong biến *fn*
hiện đang được gọi là chuyển 12 làm đối số.
Nếu bạn biết "C" ngôn ngữ lập trình một tương tự đang xem xét (let ((p #'square))...)
như lấy địa chỉ của hàm square
(như với { int (*p)(int) = □ ...}
) và thay vào đó (funcall p 12)
cũng giống như gọi một hàm sử dụng một con trỏ (như với (*p)(12)
rằng "C" cho phép viết tắt là p(12)
).
Phần admittely khó hiểu trong Common Lisp là bạn có thể có cả một hàm có tên square
và một biến có tên square
trong phạm vi tương tự và biến sẽ không ẩn các chức năng. funcall
và function
là hai công cụ bạn có thể sử dụng khi bạn cần sử dụng giá trị của một biến làm hàm hoặc khi bạn muốn một hàm làm giá trị tương ứng.
Tôi nghĩ rằng lời giải thích của bạn có thể ít hơn «thần bí» nếu bạn đã sử dụng từ 'không gian tên' thay vì từ mơ hồ' nghĩa '. CL là một Lisp-2 có nghĩa là một biểu tượng có thể xuất hiện trong cả hai không gian tên với giá trị khác nhau trong mỗi. tức là biểu tượng 'foo' có thể có giá trị 42 trong không gian tên giá trị (bạn có thể xem giá trị của nó bằng' symbol-value') và nó cũng có thể có giá trị '(lambda (x) (1+ x))' trong không gian tên hàm ('symbol-function'). – Daimrod
Cảm ơn bạn vì điều đó. Tôi tự hỏi. Trong trường hợp của bạn, bạn đã chuyển hàm vào một biến toàn cầu. Nếu bạn có chức năng trong đó, bạn có thể cũng không làm (\ * fn \ * 12)? hoặc là chỉ cho hàm vuông, tồn tại như một hàm? – Andy
@Andy: dạng '(* fn * 12)' có nghĩa là trong lisp thông thường "gọi hàm có tên' * fn * 'chuyển 12 làm tham số" ... hàm được gọi là cố định về mặt logic và thậm chí có thể được gạch chân bởi trình biên dịch: là hàm '(defun * fn * (x) ...)'. Biến được đặt tên theo cùng một cách là một thứ không liên quan khác và có thể chứa bất kỳ giá trị nào ... ví dụ một số, một chuỗi hoặc thậm chí là địa chỉ của một hàm. Biểu mẫu '(funcall * fn * 12)' có nghĩa là "gọi hàm có địa chỉ được chứa trong biến có tên' * fn * '". Ở phía bên kia '# 'square' có nghĩa là, nhiều hay ít," địa chỉ của hàm' square '". – 6502