Common Lisp譯本筆記4之第五章 函數

所有的lisp程序的最基本的組成:函數、變量、宏。

函數用宏defun定義
格式:(defun 函數名 (形參列表) 函數體)
如:(defun say (str) (format t "~A" str))

約定:
1、一般類型轉換的函數會在名字中使用→
2、將一個字符串轉爲微件(widget)的函數會叫做string->widget
3、函數名中的連接符不是下劃線,而是橫線。

在函數中緊跟這個形參列表之後的字符串可作爲描述函數用途的文檔字符串。
可通過函數documentation獲取。

函數將最後一個表達式的值作爲整個函數的返回值。也可用return-from在函數任何位置立即返回。

函數形參列表

必要參數:不用關鍵字修飾的形參,一般爲必要參數。當函數調用時,必須爲其提供一個實參,每個形參綁定對應的實參。若實參個數過多或過少,則會報錯。

可選參數:形參前用關鍵字&optional修飾。在所有必要形參都被賦值之後,若還有剩餘實參,則被賦給可選形參。若未被賦值的可選形參會自動綁定值nil。可設置默認值,用列表表示。如:(b 10)。若可選形參未傳值,則使用默認值。若檢測是否使用默認值,可用形參名加-supplied-p後綴來判斷。如:(b 10 b-supplied-p)。當使用默認值爲nil,不使用默認值爲T
例子:

(defun test (a &optional (b 10 b-supplied-p))
  (format t "a: ~A b: ~A flag: ~A" a b b-supplied-p))
測試代碼:

CL-USER> (test 2)
a: 2 b: 10 flag: NIL
NIL
CL-USER> (test 2 4)
a: 2 b: 4 flag: T
NIL


剩餘參數:形參前用關鍵字&rest修飾。若當實參個數滿足必要形參和可選形參時,剩餘實參會被放進一個列表作爲剩餘形參的值。

例子:

(defun test (a &optional b &rest values)
  (format t "a: ~A b: ~A values: ~A" a b values))

測試代碼:

CL-USER> (test 1 2 3 4 5 6)
a: 1 b: 2 values: (3 4 5 6)
NIL


關鍵字參數:形參前用關鍵字&key修飾。在函數調用時可用:形參名(即關鍵字)來給特定的形參賦值。此形參也可設置默認值及判斷是否使用默認值。用法和可選參數相同。還可以通過列表設置關鍵字別名,在函數調用時,只能通過別名來賦值。如:((:app a))

例子:

(defun test (a &key ((:bp b)) ((:cp c) 3 c-supplied-p) )
  (format t "a: ~A b: ~A c: ~A c-flag: ~A" a b c c-supplied-p))
測試代碼:

CL-USER> (test 1 :bp 2)
a: 1 b: 2 c: 3 c-flag: NIL
NIL
CL-USER> (test 1 :cp 4)
a: 1 b: NIL c: 4 c-flag: T
NIL
CL-USER> (test 1 :cp 3)
a: 1 b: NIL c: 3 c-flag: T
NIL

混合使用不同形參類型

混合使用不同形參類型時的聲明順序:必要參數、可選參數、剩餘參數、關鍵字參數。

一般組合使用的情況:必要參數和其他的一種類型組合使用。

關鍵字參數和剩餘參數、可選參數組合使用時會出現奇怪的行爲,應避免一起使用。(若未給可選參數提供值,則會將關鍵字參數的關鍵字和值作爲可選參數。)

也有可以組合使用的例子,但暫時先不考慮。


可選參數僅適用於一些較爲分散且不確定調用者會提供值的形參。
剩餘參數適用於接收可變數量的實參。
關鍵字參數適用於給指定參數賦值。


函數返回值

函數默認會將最後一個表達式的值作爲整個函數的返回值。

可使用return-from使函數在特定位置返回。第一個參數爲在返回函數中的函數名,第二個參數爲返回值。


作爲數據的函數--高階函數

一般使用函數名來調用函數,若將函數看成數據則可將函數作爲參數傳給另一個函數。

在lisp中,函數是另一種類型的對象。
用defun定義一個函數時,創建一個新的函數對象及賦予其一個名字。
使用特殊操作符function可獲取一個函數的函數對象。接收一個參數並返回參數同名的函數對象。
functiong的語法糖爲:#'


通過函數對象調用函數的兩個函數:funcall和apply
funcall:用於知道傳遞給函數的實參個數。第一個參數爲:被調用的函數對象,其餘的參數爲傳入函數的參數。
apply:第一個參數是被調用的函數對象。第二個參數是一個列表,將傳入被調函數的參數放到一個列表中。
funcall和apply的區別:funcall應用到被調函數上的參數爲單一參數,而apply則將一個列表作爲應用參數。

例子:

(defun test-fun (fn)
  (funcall fn 1 2 3))
(defun test-app (fn)
  (apply fn '(1 2 3)))
測試代碼:

CL-USER> (test-fun #'+)

6
CL-USER> (test-app #'+)
6

匿名函數

使用lambda表達式可創建一個匿名函數。第一個參數是形參列表,第二個參數是函數體
格式:(lambda (形參列表) 函數體)
lambda表達式重要用途是製作閉包,即捕捉了其創建時環境信息的函數。
例子:
(defun test (x y)
  (lambda (x y) (+ x y)) x y)
測試代碼:

CL-USER> (test 1 2)

2




發佈了79 篇原創文章 · 獲贊 20 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章