2012-01-08 33 views
8

Tôi chỉ đang học ANSI Common Lisp (sử dụng clisp trên máy Win32) và tôi đã tự hỏi liệu mapcar có thể sử dụng một hàm được truyền vào như một đối số chính thức không? Hãy thấy như sau:Lập trình bậc cao hơn với Lisp: Chuyển chức năng đến bản đồ?

(defun foo (fn seq) 
    (mapcar #'fn seq)) 

này sẽ, theo ý kiến ​​của tôi, cung cấp một mức độ cao hơn của tính linh hoạt hơn:

(defun mult (i) 
    (* i 2)) 

(defun foo() 
    (mapcar #'mult '(1 2 3))) 

Trả lời

3

Chắc chắn, bạn có thể làm điều này:

(defun foo (fn) 
    (mapcar fn '(1 2 3))) 

Ví dụ:

(foo #'(lambda (x) (* x 2))) 
(foo #'1+) 
(foo #'sqrt) 
(foo #'(lambda (x) (1+ (* x 3)))) 
28

Đây là abs olutely có thể! Bạn đã gần tới. Bạn chỉ cần chạm vào các không gian tên kép của Common Lisp, có thể mất nhiều thời gian để làm quen. Tôi hy vọng tôi có thể nói một hoặc hai điều để làm cho hai không gian tên Common Lisp của một chút ít khó hiểu.

Mã của bạn gần như chính xác. Bạn đã viết:

(defun foo (fn seq) 
    (mapcar #'fn seq)) 

Nhưng, điều đó đang cố gắng làm gì? Vâng, # ' là viết tắt. Tôi sẽ mở rộng nó ra cho bạn.

(defun foo (fn seq) 
    (mapcar (function fn) seq)) 

Vì vậy, # 'biểu tượng là viết tắt cho (biểu tượng chức năng). Trong Common Lisp - như bạn dường như đã biết - các ký hiệu có thể bị ràng buộc với một hàm và một biến; đây là hai không gian tên mà Lispers nói rất nhiều về: không gian tên hàm và không gian tên biến.

Bây giờ, chức năng đặc biệt là lấy hàm được liên kết với biểu tượng, hoặc nếu bạn muốn, giá trị biểu tượng có trong không gian tên hàm.

Bây giờ, trên REPL, những gì bạn đã viết sẽ rõ ràng là những gì bạn muốn.

(mapcar #'car sequence) 

Hãy lập bản đồ xe hoạt động theo chuỗi danh sách. Và biểu tượng xe không có ràng buộc biến, chỉ có chức năng ràng buộc, đó là lý do bạn cần sử dụng (chức năng ...) (hoặc viết tắt, # ') để có được chức năng thực tế.

Chức năng foo của bạn không hoạt động vì chức năng bạn truyền nó làm đối số đang bị ràng buộc với một biểu tượng dưới dạng biến. Hãy thử điều này:

(let ((fn #'sqrt)) 
    (mapcar #'fn '(4 9 16 25))) 

Bạn có thể đã mong đợi một danh sách tất cả các căn bậc hai của những số đó, nhưng nó không hoạt động. Đó là vì bạn đã sử dụng để liên kết chức năng gốc hình vuông với fn làm biến. Bây giờ, hãy thử mã này:

(let ((fn #'sqrt)) 
    (mapcar fn '(4 9 16 25))) 

Thú vị!Điều này liên kết chức năng gốc hình vuông với ký hiệu fn dưới dạng biến.

Vì vậy, chúng ta hãy đi xem xét lại foo bạn chức năng:

(defun foo (fn seq) 
    (mapcar fn seq)) 

Đó sẽ làm việc, vì fn là một biến. Hãy kiểm tra nó, chỉ để chắc chắn:

;; This will not work 
(foo sqrt '(4 9 16 25)) 
;; This will work 
(foo #'sqrt '(4 9 16 25)) 

Người đầu tiên đã không làm việc, bởi vì hàm căn bậc hai là ràng buộc để sqrt trong không gian tên chức năng. Vì vậy, trong phần thứ hai, chúng tôi nắm lấy chức năng từ biểu tượng, và chuyển nó đến foo, gắn nó với biểu tượng fn làm biến.


Được rồi, vậy điều gì sẽ xảy ra nếu bạn muốn liên kết hàm với ký hiệu trong không gian tên chức năng? Vâng, để bắt đầu, defun thực hiện điều đó, vĩnh viễn. Nếu bạn muốn tạm thời, chẳng hạn như , hãy để ràng buộc, sử dụng flet. Flet, theo ý kiến ​​của tôi, ngu ngốc, bởi vì nó không hoạt động chính xác như cho phép. Nhưng, tôi sẽ đưa ra một ví dụ, chỉ để bạn có thể nhìn thấy.

(flet ((fn (x) (sqrt x))) 
    (mapcar fn '(4 9 16 25))) 

sẽ không làm việc, bởi vì flet không ràng buộc chức năng để biểu tượng trong không gian tên khác nhau, nhưng trong không gian tên chức năng.

(flet ((fn (x) (sqrt x))) 
    (mapcar #'fn '(4 9 16 25))) 

này sẽ làm những gì bạn mong đợi, bởi vì flet ràng buộc chức năng đó để biểu tượng fn trong không gian tên chức năng. Và, chỉ cần lái xe ý tưởng của nhà chức năng không gian tên:

(flet ((fn (x) (sqrt x))) 
    (fn 16)) 

Sẽ trở lại 4.

+0

1 Wow, đó là khá một số câu trả lời. Rất chi tiết. :-) –

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