by shigemk2

当面は技術的なことしか書かない

関数定義を拡張する

Common LIspの拡張関数defun*を使うと、
&optional デフォルト引数
&key キーワード引数
リスト分解機能

が使えるようになる。

;; 拡張されたデフォルト値
(defun* test-default (a &optional (b 2) (c 3 c-avail))
  (list a b c c-avail))
(test-default 1)			; => (1 2 3 nil)
(test-default 1 5)			; => (1 5 3 nil)
(test-default 1 5 7)			; => (1 5 7 t)
;; キーワード引数
(defun* test-keyword (&key a (b 2))
  (list a b))
(test-keyword)				; => (nil 2)
(test-keyword :a 3)			; => (3 2)
(test-keyword :a 3 :b 5)		; => (3 5)
;; 他のキーワードを指定したらエラーになる
(ignore-errors (test-keyword :a 3 :b 5 :c 7)) ; => nil
;; 他のキーワードも受けつけるようにする
;; &allow-other-keysを使う
(defun* test-other-keyword (&rest args &key a &allow-other-keys)
  (list args a (plist-get args :y)))
(test-other-keyword :a 2)		; => ((:a 2) 2 nil)
(test-other-keyword :x 0 :y 1)		; => ((:x 0 :y 1) nil 1)
(test-other-keyword 1 2 3)		; => ((1 2 3) nil nil)
;; 省略可能引数とキーワード引数
(defun* test-many-args (a &optional b &key c)
  (list a b c))
(test-many-args 1)			; => (1 nil nil)
(test-many-args 1 2)			; => (1 2 nil)
(test-many-args 1 2 :c 3)		; => (1 2 3)
;; これはエラー
(ignore-errors (test-many-args 1 2 3))	; => nil
;; リスト分解機能
(defun* test-destructure (a (b c (d)) e)
  (list a b c d e))
(test-destructure 1 '(2 3 (4)) 5)	; => (1 2 3 4 5)

;; 複数の関数を使ってキーワード引数を解析する例
(defun* outer (&rest args &key a &allow-other-keys)
  (princ (format "a=%s " a))
  ;; 残りのキーワード引数の解析をinnerに任せる
  (apply 'inner :allow-other-keys t args))
(defun* inner (&rest args &key b)
  (princ (format "b=%s" b)))
(with-output-to-string
  (outer 8 :a 1 :b 2 :c 3))		; => "a=1 b=2"

P193

Emacs Lispテクニックバイブル

Emacs Lispテクニックバイブル