研究下Scheme和Lisp在lamba上的區別。今天晚上特別鬱悶。本來想體會一下Lisp中函數作爲第一等變量等柑橘,想不到卻不知道載了多少個跟頭。
Scheme
Scheme作爲Lisp的一個方言,由MIT研發,並且GPL的協議下發布。大家可以從GNU官網上下載其源碼,然後編譯安裝。Scheme相對與Lisp結合了Agol(不確定)。我們現在來看一下載Scheme語言中如何返回一個函數:
(define (repeat-2 fun)
(lambda (x) (fun (fun x)))
上述定義的函數將函數fun的功能重複兩遍。其使用方法如下,假設定義了加1函數incf,
((repeat-2 incf) 1)
這樣就將incf重複了兩次。
Lisp
如果對Scheme的使用方式比較熟悉,那麼在使用lisp實現時會覺得很奇怪。因爲Lisp解析器根據變量的位置不同來決定變量是函數還是變量。Lisp中由兩個函數boundp和fboundp,分別判斷一個名字是否時一個有值的變量或者函數。載Lisp中lambda是宏,其展開時會調用function。舉個例子。
(defun repeat-2 (fun)
(lambda (x) (funcall fun (funcall fun x))))
此時解析器會將其展開成如下:
(defun repeat-2 (fun)
(function lambda (x) (funcall fun (funcall fun x))))
Lisp允許我們只寫lambda只是提供了一個語法糖,允許我們直接使用。注意到將函數作爲變量值傳遞給函數時,調用方式不能像Scheme那樣直接調用,而是需要funcall或者apply來調用。repeat-2返回將指定的函數重複2次的功能,返回的函數值,我們不能直接使用,也必須使用funcall或者apply。相對於Scheme好像有點複雜。
(funcall (repeat-2 #'incf) 1)
今晚在測試的時候使用Lisp系統自帶的incf,結果報錯,說incf是宏,不是函數。當時沒注意,以爲是調用方式出問題,便加了#'。後來又報錯說值不能放函數定義。後來回過頭來想起,原來incf是,他們封裝了set的功能。於是自己寫了incf-my,然後按照上面的方式執行一遍,結果OK。
後來Google搜索了相關的使用方法。原來Lisp中有兩個Cell,姑且成爲function cell和alue cell。所以允許函數名字和變量名字相同,解析器是根據位置來確定到底時解析成函數還是變量。通過之前說的boundp和fboundp來判斷到底是變量還是函數。
本文完。