2012-03-08 36 views
5

Tôi đang đọc qua một cuốn sách để làm bài tập về nhà và tôi hiểu rằng việc sử dụng # 'đang coi biến là một hàm thay vì một biến. Nhưng tôi hơi mơ hồ về FUNCALL. Tôi hiểu rằng lisp làm cho đối tượng ra khỏi các biến, như vậy là tên hàm chỉ là một 'con trỏ' (có thể là một từ xấu, nhưng hy vọng bạn sẽ có được những gì tôi có ý nghĩa), trong trường hợp bạn sử dụng # 'để gọi nó, hoặc là funcall cách duy nhất để gọi họ là gì? ví dụSự khác nhau giữa FUNCALL và # 'function-name trong lisp thông thường là gì?

(defun plot (fn min max step) 
(loop for i from min to max by step do 
     (loop repeat (funcall fn i) do (format t "*")) 
     (format t "~%"))) 

thể không Tôi chỉ làm:

(defun plot (fn min max step) 
(loop for i from min to max by step do 
     (loop repeat #'(fn i) do (format t "*")) 
     (format t "~%"))) 

Tôi đoán nhầm lẫn của tôi nằm trong những gì chính xác là trong tên hàm. Khi tôi đọc cuốn sách, nó nói rằng giá trị của biến là những gì sẽ là đối tượng hàm.

Trả lời

13

#'function-name(function function-name). Không có gì được gọi, đánh giá kết quả trong hàm kết hợp với function-name (đối tượng đại diện cho hàm). funcall được sử dụng để gọi hàm.

Xem funcallfunction trong HyperSpec.

phiên Sample sử dụng cả hai:

CL-USER> (defun square (x) (* x x)) 
SQUARE 
CL-USER> #'square 
#<FUNCTION SQUARE> 
CL-USER> (function square) 
#<FUNCTION SQUARE> 
CL-USER> (funcall #'square 3) 
9 
CL-USER> (funcall 'square 3) 
9 

Các gọi thứ hai của funcall công trình vì nó cũng chấp nhận một biểu tượng như chức năng định danh (xem liên kết cho funcall trên để biết chi tiết).

8

Các #'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ư

  1. 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
  2. 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ụ:

lisp-2

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 #'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) = &square; ...}) 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. funcallfunction 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.

+4

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

+0

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

+0

@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

Các vấn đề liên quan