2013-02-10 58 views
5

我有一組名爲「ip」,「date」,「url」等功能。程序功能定義:如何在這裏擺脫「eval」?

有了這些,我想生成另一組函數「ip-is」,「date-is」等。

我終於有了下面的解決方案,那工作正常,但使用「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)))))) 

有人可以幫助我,如何擺脫「邪惡eval」?對於我的程序而言,函數名稱作爲列表提供是非常重要的。所以在一定marcro

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

等調用

不適合我的需要。我對eval沒有任何問題,但我經常閱讀,認爲eval被認爲是不好的風格,應儘可能避免。

在此先感謝!

回答

7

你應該在這裏使用宏。宏在編譯(或加載)過程中進行評估,並可用於以編程方式生成函數定義。您的代碼可以寫這樣的:

(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) 

注意,符號在函數中使用GENSYM產生。在這個特殊情況下,這並不是絕對必要的,但我通常更喜歡這樣做,以便在稍後階段重構代碼時不會有任何泄漏的機會。

+0

絕對是宏正確的情況下。使用'eval'作爲正確的選擇是非常罕見的情況。 – 2013-02-10 16:42:22

+0

@Elias,只是一個關於你寫的內容的問題:在我看來,宏在加載時不能被評估,因爲它們在編譯時被用來生成代碼。 – tuscland 2013-02-12 18:31:50

5

如果你想使用的功能(而不是宏在對方的回答),你應該使用(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

嗨,這正是我正在尋找的,但由於某種原因,它不適合我。我瞭解解決方案,但我不明白,爲什麼它不工作。我正在使用sbcl 1.0.58 – Patrick 2013-02-11 00:35:24

+0

@Patrick:它是如何失敗的? – sds 2013-02-11 01:26:03

+0

它編譯,如果我打電話(代碼是「200」)它給了我一個謂詞閉包。只要一切都很好。但是如果我用我的過濾器例程調用它,當功能「代碼」提供「200」時,它不能正確匹配大小寫。如果我直接稱之爲例如。 (defun code(z)「200」),然後運行你的定義,然後(funcall(code-is「200」)「200」)它打破「INVALID-ARRAY-INDEX-ERROR」。這很奇怪,因爲我們沒有在這裏處理數組。 – Patrick 2013-02-11 09:59:28