apply の引数
apply: FUNCTION ARG &rest MORE-ARGS
apply は何気に引数を複数に分けて受けられる。でも funcall とは違って、最初見たときは意味がわからん動作をする。
というのは、ARG と MORE-ARGS のうち一番最後の引数は list に突っ込んでおかねばならず、それ以外はそのままでいいというかそのままでないといけない。
#| ;;; 最初は受け取った引数をリストにまとめて返すこんな関数を ;;; 定義して使ってたのだけど、list がまさに同じ動作をする ;;; ことに気づいてしまった (defun args (&rest args) args) |# ;;; ふつーに関数 list を呼び出す (list 1 2 3) => (1 2 3) ;;; ふつーに関数 list を apply で呼び出す (apply #'list '(1 2 3)) => (1 2 3) ;;; 引数をバラしてみると (apply #'list 1 2 3) => (1 2) ; 3 はいずこへ ;;; (list への)最初の引数をリストで渡すと (apply #'list '(1 2) 3) => ((1 2)) ; (?_?) ;;; これが正解 (apply #'list 1 2 '(3)) => (1 2 3)
それで何ができるか
たまに便利なときがある。いくつかの値をリストでまとめて持っていて、それをバラバラの引数として渡したいとき。特に &rest や &key でいくつでも引数を受け取る関数に渡すとき。
;;; &rest で受け取った値をそのまま format に渡す (defun my-print (fmt &rest args) (apply #'format t fmt args)) => print-to-standard-output (my-print "~S, ~S, ~S" :foo :bar :baz) :foo, :bar, :baz => nil
あと、xyzzy だとよく見るのが set-text-attribute するとき。
;;; 色とかは設定用の変数で変更できるようにしておいて (defvar *your-favorit-attribute* '(:foreground 3 :bold t)) ;;; 呼び出すときは範囲とタグを指定+変数で apply (apply #'set-text-attribute from to tag *your-favorit-attribute*)
ちなみに引数は最低1つ(apply の引数としては2つ)は必要。
(apply #'list) !! 引数が少なすぎます: (apply #<function: list>)
apply はいったいなにをしているのか?
apply は、第2引数以降に受けた引数をケツから cons していって、最終的にひとつのリストにまとめる(ような動作をする)。
(apply #'list 1 2 '(3)) => (1 2 3) (apply #'list (cons 1 (cons 2 '(3)))) => (1 2 3) ;;; ちなみに、こうなので (cons 1 (cons 2 '(3))) => (1 2 3) ;;; これと同じことになる (apply #'list '(1 2 3)) => (1 2 3)
なので、全部バラバラに引数を指定しておいて、更に nil を追加するなんて方法も使える。
(apply #'list 1 2 3 nil) => (1 2 3) (cons 1 (cons 2 (cons 3 nil))) => (1 2 3)
というわけで
(defun apply-arg-folding (arg &rest more-args) (let ((args (cons arg more-args))) (reduce #'cons (butlast args) :from-end t :initial-value (car (last args))))) => apply-arg-folding (apply-arg-folding 1 2 '(3)) => (1 2 3)