2012-01-08 44 views
8

我剛剛學習ANSI Common Lisp(在Win32機器上使用clisp),我想知道mapcar是否可以使用傳入的函數作爲正式參數?請參閱以下內容:使用Lisp進行高階編程:將函數傳遞給mapcar?

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

這,在我看來,提供了更大的靈活性水平比:

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

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

回答

3

當然,你可以這樣做:

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

例子:

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

這是abs完全可能!你快到了。你碰到Common Lisp的雙重命名空間,這可能需要很多習慣。我希望我能說一兩句讓Common Lisp的兩個命名空間不那麼困惑。

你的代碼幾乎是正確的。你寫道:

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

但是,那是在做什麼?那麼,#'是速記。我會爲你擴展它。

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

所以,#「符號是簡寫(函數符號)。在Common Lisp中 - 正如你所知道的那樣 - 符號可以綁定到一個函數和一個變量;這些是Lispers談論得非常多的兩個命名空間:函數名稱空間和變量名稱空間。

現在,函數特殊形式所做的是獲取綁定到符號的函數,或者,如果您願意,函數名稱空間中符號具有的值。

現在,在REPL上,你寫的東西顯然是你想要的。

(mapcar #'car sequence) 

請問功能映射到列表的順序。和汽車符號沒有可變綁定,只有一個函數綁定,這就是爲什麼你需要使用(函數...)(或其簡寫,#')來獲得實際功能。

功能不起作用,因爲你把它作爲一個參數的函數被綁定到一個符號作爲變量。試試這個:

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

你可能期望所有這些數字平方根的名單,但沒有奏效。這是因爲您使用將平方根函數綁定爲fn作爲變量。現在,試試這個代碼:

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

令人愉快!這將平方根函數與作爲變量的符號綁定在一起。

所以,讓我們去修改你的功能:

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

的工作,因爲FN是可變的。讓我們測試一下,只是爲了確保:

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

第一個沒有工作,因爲平方根函數被綁定到函數命名空間開方。因此,在第二個中,我們從符號中抓取了函數,並將其傳遞給foo,並將其作爲變量綁定到符號fn


好了,所以如果你想要的功能綁定到函數命名空間的象徵?那麼,對於初學者來說,defun這樣做是永久性的。如果你想要它是暫時的,就像綁定,使用fletFlet在我看來是愚蠢的,因爲它不能像let一樣工作。但是,我會舉一個例子,讓你可以看到。

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

將無法​​正常工作,因爲FLET沒有的功能綁定到變量命名空間中的符號,但在功能的命名空間。

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

這將做你所期望的,因爲FLET約束,且功能的函數命名空間的符號FN。而且,只是驅動函數命名空間的家的想法:

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

將返回4.

+0

+1哇,那是相當的一些答案。很詳細。 :-) – 2012-01-08 04:44:23