2012-03-15 11 views
14

Tại sao chúng ta phải sử dụng funcall để gọi các hàm bậc cao hơn trong Common Lisp? Ví dụ, tại sao chúng ta phải sử dụng:Tại sao chúng ta cần funcall trong Lisp?

(defun foo (test-func args) 
    (funcall test-func args)) 

thay vì đơn giản:

(defun bar (test-func args) 
    (test-func args)) 

Xuất thân từ một nền tố tụng, tôi hơi ngạc nhiên bởi rằng kể từ khi ngôn ngữ tôi hơn được sử dụng để (ví dụ như Python, C#) không cần sự khác biệt. Đặc biệt, ở cấp nguồn ít nhất, trình biên dịch C# biến đổi nó thành một cái gì đó như func.invoke().

Vấn đề duy nhất tôi thấy là điều này có nghĩa là chúng tôi không thể gọi chức năng toàn cầu test-func nữa vì nó sẽ bị che khuất, nhưng đó không phải là vấn đề.

+3

"Khó có vấn đề" là mấu chốt của vấn đề. Trong thực tế, tôi thấy khó có thể gọi funcall và sẽ bất tiện nếu tôi phải chọn tên sáng tạo/kỳ lạ cho các biến để tránh xung đột với CAR, DANH SÁCH, REST, v.v. – Xach

Trả lời

16

Nói đúng ra, funcall sẽ không cần thiết, nhưng có một số lisps (list-2 triển khai, chẳng hạn như Common Lisp) mà tách không gian tên biến của tên chức năng không gian. Triển khai danh sách 1 (ví dụ: Đề án) không tạo nên sự khác biệt này.

Cụ thể hơn, trong trường hợp của bạn, test-func nằm trong không gian tên biến.

(defun foo (test-func args) 
    (funcall test-func args)) 

Vì vậy, bạn cần một cấu trúc thực sự tìm kiếm đối tượng hàm liên kết với biến này trong không gian tên biến. Trong Common Lisp, cấu trúc này là funcall.

Xem thêm this answer.

12

Phần lớn Lisps có hai không gian tên (hàm và biến). Một tên được tra cứu trong không gian tên hàm khi nó xuất hiện như là phần tử đầu tiên trong một biểu thức S và trong không gian tên biến khác. Điều này cho phép bạn đặt tên biến của mình mà không phải lo lắng về việc chúng có chức năng đổ bóng hay không: vì vậy bạn có thể đặt tên biến là list thay vì phải biến nó thành lst.

Tuy nhiên, điều này có nghĩa là khi bạn lưu trữ một hàm trong một biến, bạn không thể gọi nó là bình thường:

(setq list #'+) ; updates list in the variable namespace 
(list 1 2 3) => (1 2 3) ; looks up list in the function namespace 

Do đó nhu cầu funcallapply:

(funcall list 1 2 3) => 6 ; looks up list in the variable namespace 

(Không tất cả Lisps có hai không gian tên: Lược đồ là một ví dụ về một Lisp chỉ với một không gian tên.)

+0

Có lợi thế của các chức năng không đổ bóng là tốt, nhưng mặt khác, chúng tôi nhận được các trường hợp đặc biệt cho các chức năng .. tôi cho rằng vì chúng ta không phải lo lắng về việc định vị khung trong lisp, chúng ta cần các cuộc chiến tôn giáo khác;) Vì bạn và Diego đều trả lời câu hỏi của tôi một cách độc đáo, tôi đã đi với thời gian như một người kết nối và chấp nhận câu trả lời của người chết. – Voo

-2

Trong Lisp chung, mỗi biểu tượng có thể được liên kết với nó symbol-functionsymbol-value của nó, trong số những thứ khác.Khi đọc một danh sách, theo mặc định, Common Lisp giải thích:

  • arg1 như một chức năng và do đó lấy test-func 's symbol-function, mà là undefined - do đó chức năng bar không hoạt động
  • arg2 như một cái gì đó để được eval ed - do đó chức năng foo lấy test-func 's symbol-value, trong đó, trong trường hợp của bạn, sẽ xảy ra là một hàm
+0

đây không phải là những gì mà triển khai Common Lisp thực hiện. Bạn có thể muốn đọc về các ràng buộc từ vựng. –

1

Lưu ý rằng trong bất kỳ Lisp, nếu bạn muốn gọi một hàm trong một số cách khác hơn

(function fixed arg u ments) 

bạn phải sử dụng thứ gì đó ở vị trí đầu tiên của biểu mẫu ngoài chức năng. Trong một số tiếng địa phương, nếu F là một biến mà giữ một chức năng, bạn có thể chỉ làm điều này:

(f a b c) ;; no funcall 

Nhưng trong khá nhiều tất cả các phương ngữ, bạn không thể loại bỏ các apply

(apply f args-list) ;; Common Lisp or Scheme: same 

Nếu bạn đang chạy vào funcall là một mối phiền toái, bạn chỉ không sử dụng đủ ứng dụng thú vị trên các đối số chức năng của bạn. :)