2013-02-10 23 views
5

Tôi có một tập hợp các chức năng có tên là "ip", "ngày", "url", v.v.Định nghĩa chức năng chương trình: Cách loại bỏ "eval" ở đây?

Với những điều này, tôi muốn tạo một tập hợp các hàm khác "ip-is", "date-is" v.v ...

Cuối cùng tôi có giải pháp sau, thats làm việc tốt, nhưng sử dụng "eval".

(loop for name in '(ip date url code bytes referer user-agent) do 
    (let ((c-name (intern (concatenate 'string (symbol-name name) "-IS")))) 
    (eval `(defun ,c-name (c) 
      #'(lambda (l) (equal (,name l) c)))))) 

Ai đó có thể giúp tôi, cách loại bỏ "bad evil"? Điều quan trọng đối với chương trình của tôi là tên hàm được cung cấp dưới dạng danh sách. Vì vậy, một cuộc gọi đến một số marcro

(define-predicate ip) 
    (define-predicate date) 
    (define-predicate url) 

, vv

sẽ không phù hợp với nhu cầu của tôi. Tôi không có vấn đề thực sự với "eval", nhưng tôi đọc rất thường xuyên, rằng eval được coi là phong cách xấu và nên tránh nếu có thể.

Cảm ơn bạn đã nâng cao!

Trả lời

7

Bạn nên sử dụng macro tại đây. Các macro được đánh giá trong quá trình biên dịch (hoặc tải) và có thể được sử dụng để tạo ra một định nghĩa hàm một cách có lập trình. Mã của bạn có thể được viết như sau:

(defmacro define-predicates (&rest names) 
    `(progn 
    ,@(loop 
      for name in names 
      collect (let ((c-sym (gensym)) 
         (l-sym (gensym))) 
        `(defun ,(intern (concatenate 'string (symbol-name name) "-IS")) (,c-sym) 
         #'(lambda (,l-sym) (equal (,name ,l-sym) ,c-sym))))))) 


(define-predicates ip date url) 

Lưu ý rằng các ký hiệu được tạo bằng cách sử dụng GENSYM trong các chức năng. Trong trường hợp đặc biệt này, điều đó là không cần thiết, nhưng tôi thường thích làm theo cách này để không có cơ hội bị rò rỉ nếu tôi tái cấu trúc lại mã ở giai đoạn sau.

+0

Chắc chắn các trường hợp phù hợp với các macro. Sử dụng 'eval' là sự lựa chọn đúng là một trường hợp rất hiếm. –

+0

@Elias, chỉ là một câu hỏi về những gì bạn đã viết: có vẻ như tôi không thể đánh giá macro tại thời điểm tải vì chúng được sử dụng tại thời gian biên dịch để tạo mã. – tuscland

5

Nếu bạn muốn sử dụng một chức năng (thay vì một vĩ mô như trong câu trả lời khác), bạn nên sử dụng (setf fdefinition):

(loop for name in '(ip date url code bytes referer user-agent) do 
    (let ((c-name (intern (concatenate 'string (symbol-name name) "-IS")))) 
    (setf (fdefinition c-name) 
      (lambda (c) (lambda (l) (equal (funcall name l) c)))))) 
+0

Xin chào, đây là chính xác, những gì tôi đang tìm kiếm, nhưng vì một lý do nào đó, nó không hoạt động cho tôi. Tôi hiểu các giải pháp nhưng tôi không thể tìm ra, tại sao nó không hoạt động. Tôi đang sử dụng sbcl 1.0.58 – Patrick

+0

@Patrick: Làm thế nào nó thất bại? – sds

+0

nó biên dịch và nếu tôi gọi (mã-là "200") nó mang lại cho tôi một vị thế đóng cửa. Vì vậy, tất cả mọi thứ dài là tốt. Nhưng nếu tôi gọi nó với thói quen lọc của tôi, nó không phù hợp với trường hợp một cách chính xác khi hàm "mã" cung cấp "200". Nếu tôi gọi nó là trực tiếp, ví dụ như. (defun mã (z) "200"), sau đó chạy định nghĩa của bạn, sau đó (funcall (mã-là "200") "200") nó phá vỡ với "INVALID-ARRAY-INDEX-ERROR". Thats khá lạ, vì chúng ta không giao dịch với mảng ở đây. – Patrick

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