common-lisp
ファーストクラスの値としての機能
サーチ…
構文
- (関数名);その名前の関数オブジェクトを取得する
- # 'name;構文の砂糖(関数名)
- (シンボル機能シンボル)。シンボルにバインドされた関数を返します。
- (funcall関数のargs ...); argsを使って関数を呼び出す
- (関数arglistを適用する);引数をリストに指定して関数を呼び出す
- (関数arg1を適用するarg2 ... argn arglist); arg1、arg2、...、argnによって与えられた引数を持つ関数をコールし、残りはリストarglistに入れます
パラメーター
パラメータ | 詳細 |
---|---|
名 | 関数に名前を付ける(評価されていない)記号 |
シンボル | シンボル |
関数 | 呼び出される関数 |
args ... | ゼロ個以上の引数(引数のリストではない ) |
arglist | 関数に渡す引数を含むリスト |
arg1、arg2、...、argn | それぞれは関数に渡す単一の引数です |
備考
Lispのような言語について言及するとき、Lisp-1とLisp-2として知られているものに共通の区別があります。 Lisp-1では、シンボルは値だけを持ち、シンボルが関数を参照する場合、そのシンボルの値はその関数になります。 Lisp-2では、シンボルは別々の関連する値と関数を持つことができるため、値ではなくシンボルに格納されている関数を参照するために特別な形式が必要です。
Common Lispは基本的にLisp-2ですが、実際には2つ以上の名前空間(シンボルが参照できるもの)があります。シンボルは値、関数、タイプ、タグなどを参照できます。
匿名関数の定義
Common Lispの関数はファーストクラスの値です。無名関数は、 lambda
を使用して作成できます。たとえば、3つの引数の関数を次にfuncall
を使用して呼び出します
CL-USER> (lambda (a b c) (+ a (* b c)))
#<FUNCTION (LAMBDA (A B C)) {10034F484B}>
CL-USER> (defvar *foo* (lambda (a b c) (+ a (* b c))))
*FOO*
CL-USER> (funcall *foo* 1 2 3)
7
匿名関数は直接使用することもできます。 Common Lispには構文があります。
((lambda (a b c) (+ a (* b c))) ; the lambda expression as the first
; element in a form
1 2 3) ; followed by the arguments
匿名関数は、グローバル関数として格納することもできます。
(let ((a-function (lambda (a b c) (+ a (* b c))))) ; our anonymous function
(setf (symbol-function 'some-function) a-function)) ; storing it
(some-function 1 2 3) ; calling it with the name
引用符で囲まれたラムダ式は関数ではありません
クォートされたラムダ式はCommon Lispの関数ではないことに注意してください。これは動作しません :
(funcall '(lambda (x) x)
42)
機能使用に引用されたラムダ式を変換するには、 coerce
、 eval
かfuncall
:
CL-USER > (coerce '(lambda (x) x) 'function)
#<anonymous interpreted function 4060000A7C>
CL-USER > (eval '(lambda (x) x))
#<anonymous interpreted function 4060000B9C>
CL-USER > (compile nil '(lambda (x) x))
#<Function 17 4060000CCC>
既存の関数を参照する
Common Lispのシンボルには、変数がバインドされるスロットと、バインドされる関数のための別個のスロットがあります。
この例での命名は、説明のためだけのものであることに注意してください。グローバル変数の名前はfoo
ですが、 *foo*
べきではありません。後者の表記法は、変数が動的バインディングを使用する特殊変数であることを明確にするための規則です。
CL-USER> (boundp 'foo) ;is FOO defined as a variable?
NIL
CL-USER> (defvar foo 7)
FOO
CL-USER> (boundp 'foo)
T
CL-USER> foo
7
CL-USER> (symbol-value 'foo)
7
CL-USER> (fboundp 'foo) ;is FOO defined as a function?
NIL
CL-USER> (defun foo (x y) (+ (* x x) (* y y)))
FOO
CL-USER> (fboundp 'foo)
T
CL-USER> foo
7
CL-USER> (symbol-function 'foo)
#<FUNCTION FOO>
CL-USER> (function foo)
#<FUNCTION FOO>
CL-USER> (equalp (quote #'foo) (quote (function foo)))
T
CL-USER> (eq (symbol-function 'foo) #'foo)
T
CL-USER> (foo 4 3)
25
CL-USER> (funcall foo 4 3)
;get an error: 7 is not a function
CL-USER> (funcall #'foo 4 3)
25
CL-USER> (defvar bar #'foo)
BAR
CL-USER> bar
#<FUNCTION FOO>
CL-USER> (funcall bar 4 3)
25
CL-USER> #'+
#<FUNCTION +>
CL-USER> (funcall #'+ 2 3)
5
高次関数
Common Lispには、引数の関数を渡して呼び出す多くの上位関数が含まれています。おそらく最も基本的なのはfuncall
とapply
です。
CL-USER> (list 1 2 3)
(1 2 3)
CL-USER> (funcall #'list 1 2 3)
(1 2 3)
CL-USER> (funcall #'list 1 2 3 4 5)
(1 2 3 4 5)
CL-USER> (apply #'list '(1 2 3))
(1 2 3)
CL-USER> (apply #'list 1 2 '(4 5))
(1 2 3 4 5)
CL-USER> (apply #'+ 1 (list 2 3))
6
CL-USER> (defun my-funcall (function &rest args)
(apply function args))
MY-FUNCALL
CL-USER> (my-funcall #'list 1 2 3)
(1 2 3)
例えば、リストの要素に何度も関数を適用する、他にも多くの上位関数があります。
CL-USER> (map 'list #'/ '(1 2 3 4))
(1 1/2 1/3 1/4)
CL-USER> (map 'vector #'+ '(1 2 3 4 5) #(5 4 3 2 10))
#(6 6 6 6 15)
CL-USER> (reduce #'+ '(1 2 3 4 5))
15
CL-USER> (remove-if #'evenp '(1 2 3 4 5))
(1 3 5)
リストを集計する
reduce関数を使用して、リスト内の要素を合計することができます。
(reduce '+ '(1 2 3 4))
;;=> 10
デフォルトでは、 reduceは左連想縮小を実行します。つまり、合計10は次のように計算されます。
(+ (+ (+ 1 2) 3) 4)
最初の2つの要素が最初に合計され、次にその結果(3)が次の要素(3)に加算されて6が生成され、最後に4が加算されて最終結果が生成されます。
これが適用されます使用してより安全である(例えば、(に適用するために渡すことができる引数リストの長さが制限されるため( コールの引数制限を参照してください)「+」(1 2 3 4))を適用し、意志の仕事を減らします 2つの引数しか取らない関数があります。
from-endキーワード引数を指定すると、 reduceはリストを他の方向に処理します。つまり、合計は逆の順序で計算されます。あれは
(reduce '+ (1 2 3 4) :from-end t)
;;=> 10
コンピューティング
(+ 1 (+ 2 (+ 3 4)))
逆の実装とrevappend
Common Lispはすでに逆関数を持っていますが、そうでなければreduceを使って簡単に実装できます。のようなリストが与えられた
(1 2 3) === (cons 1 (cons 2 (cons 3 '())))
逆のリストは
(cons 3 (cons 2 (cons 1 '()))) === (3 2 1)
それはreduceの明白な使い方ではないかもしれませんが、もしxconsのような "逆のcons"関数があれば
(xcons 1 2) === (2 . 1)
その後、
(xcons (xcons (xcons () 1) 2) 3)
これは削減です。
(reduce (lambda (x y)
(cons y x))
'(1 2 3 4)
:initial-value '())
;=> (4 3 2 1)
Common Lispには、 revappendという別の便利な関数があります 。これは、 reverseとappendの組み合わせです。概念的には、リストを逆にして、それをいくつかの末尾に追加します。
(revappend '(3 2 1) '(4 5 6))
;;=> (1 2 3 4 5 6)
これはreduceで実装することもできます。実際、それは空のリストの代わりに初期値が(4 5 6)である必要があることを除いて、上記の逆の実装と同じです。
(reduce (lambda (x y)
(cons y x))
'(3 2 1)
:initial-value '(4 5 6))
;=> (1 2 3 4 5 6)
閉鎖
関数は定義されているレキシカルスコープを覚えています。このため、ラムダをletで囲んでクロージャを定義することができます。
(defvar *counter* (let ((count 0))
(lambda () (incf count))))
(funcall *counter*) ;; => 1
(funcall *counter*) ;; = 2
上記の例では、カウンタ変数には無名関数しかアクセスできません。これは次の例でより明確に見られます
(defvar *counter-1* (make-counter))
(defvar *counter-2* (make-counter))
(funcall *counter-1*) ;; => 1
(funcall *counter-1*) ;; => 2
(funcall *counter-2*) ;; => 1
(funcall *counter-1*) ;; => 3
関数を受け取り、関数を返す関数の定義
簡単な例:
CL-USER> (defun make-apply-twice (fun)
"return a new function that applies twice the function`fun' to its argument"
(lambda (x)
(funcall fun (funcall fun x))))
MAKE-APPLY-TWICE
CL-USER> (funcall (make-apply-twice #'1+) 3)
5
CL-USER> (let ((pow4 (make-apply-twice (lambda (x) (* x x)))))
(funcall pow4 3))
81
関数合成の古典的な例:( f・ g・ h )( x )= f ( g ( h) ( x )):
CL-USER> (defun compose (&rest funs)
"return a new function obtained by the functional compositions of the parameters"
(if (null funs)
#'identity
(let ((rest-funs (apply #'compose (rest funs))))
(lambda (x) (funcall (first funs) (funcall rest-funs x))))))
COMPOSE
CL-USER> (defun square (x) (* x x))
SQUARE
CL-USER> (funcall (compose #'square #'1+ #'square) 3)
100 ;; => equivalent to (square (1+ (square 3)))