リストの最初の要素として関数からラムダ値を使用する
-
27-10-2019 - |
質問
私はピーター・ノーヴィグの人工知能プログラミングのパラダイムを読んでいますが、自分で解決できない問題に遭遇しました(これは私のLISPの紹介です)。問題は非常に小さなものですが、明らかに私の小さな脳が解決できるものではありません。
関数の値がラムダである場合、その関数をリストの最初の要素として使用することはエラーであるのはなぜですか。例えば:
舌足らずの発音:
(defun some-func ()
#'(lambda (x) x))
;; At REPL
;; Does not work
> ((some-func) 1)
;; Does work
> ((lambda (x) x) 1)
;; Also works
> (funcall (some-func) 1)
それが理にかなっていることを願っています!
解決
これは良い質問であり、一般的なLISPがかなり混乱する可能性のある質問です。問題は、歴史的な理由により、一般的なLISPには2つの名前空間があります。1つは機能用、もう1つは値用です。これを実現するために、関数アプリケーションのヘッド位置と残りの場合は2つの異なる評価ルールがあります。1つ目はシンボルを関数名として評価し、2つ目はシンボルを変数参照として評価します。値が実際に関数である場合、たとえば、 mapcar
機能、あなたはようなことをしたいと思うでしょう
(defun my-mapcar (f l)
(if (null l)
'()
(cons (f (car l)) (my-mapcar f (cdr l)))))
しかし、これはうまくいきません - それは不平を言うでしょう f
未知の機能であること。これらの場合には、呼ばれる特別な関数があります funcall
, 、関数の関数と引数を受け取り、通常どおり関数を適用します - そしてそれ以降 funcall
は単純な機能であり、その引数はすべて通常のように評価されます(値として)。したがって、上記はそれを使用して修正する必要があります。
(defun my-mapcar (f l)
(if (null l)
'()
(cons (funcall f (car l)) (my-mapcar f (cdr l)))))
あなたがおそらく今疑っているように、ミラーケースがあります - あなたが機能として何かを評価したいのです いいえ 値として。たとえば、これは機能しません。
(my-mapcar 1+ '(1 2 3))
それはを指すからです 1+
関数ではなく変数。これらの場合には、呼ばれる特別なフォームがあります function
それはその内容を関数として評価し、値として返します。
(my-mapcar (function 1+) '(1 2 3))
そして、それは省略することができます #'
:
(my-mapcar #'1+ '(1 2 3))
それはこの物語の終わりではありません - いくつかの例を挙げてください:
場合によっては、単純な引用名が関数として機能する可能性があります - たとえば
'1+
最後の例では動作しますが、これはグローバルにバインドされた名前のみを見ることができるハックのようなものです。#'
ほとんど常に良いです同様のハックを使用できます
lambda
- だからあなたは使用できます(my-mapcar '(lambda (x) (1+ x)) '(1 2 3))
, 、実際、使用できます(list 'lambda '(x) '(1+ x))
これはさらに悪い(そしてiirc、非ポータブル)が、使用する(lambda (x) (1+ x))
暗黙的に包まれているので動作します#'
(拡張してみてくださいlambda
マクロとして形成すれば、あなたはそれを見るでしょう)。関連するハックは、aを使用しても問題ありませんlambda
関数アプリケーションのヘッドとしての表現(これはあなたが試したものの1つです)。let
などの局所的な値を結合すると、場合によっては、代わりにローカル機能をバインドする必要があります。これには、新しい結合構造があります。flet
とlabels
これらすべてが奇妙で、/または過度に複雑に見える場合、あなたは一人ではありません。これは、一般的なLISPとスキームの主な違いの1つです。 (その後、違いは両方の言語の一般的なイディオムの変更につながります。スキームコードは、一般的なLISPコードよりもはるかに頻繁に高次関数を使用する傾向があります。この種の宗教的な質問では、CLが何をするかを支持して主張する人もいます。その高次関数は混乱しているため、明示的なコード内リマインダーが気に入っています。)
他のヒント
((lambda (x) ...) ...)
評価者ルールのハードコードされた特別なケースです。フォームの最初の要素を評価し、結果を一般的なケースとして使用することではありません。使用する必要があるのは普通です funcall
また apply
他の形式を評価した結果である関数を呼び出す。
一般的なLISPが、lambdaを式の最初の項目に戻す関数が許可されていない理由は、間の区別に関係しています LISP-1およびLISP-2.
一般的なLISPが許可されている場合 ((some-func) 1)
に相当する (funcall (some-func) 1)
, 、それからそれはまた、言うことを許可しないことと矛盾していると認識される可能性があります (let ((f #'some-func)) (f 1))
要求するのではなく (funcall f 1)
.
実際には、そのようなフォームをサポートしないことには非常に良い正当化があります。それらはあいまいです。方法を考えてください 関数名 共通のLISPで定義されています:
関数名 n. 。 1.(An 環境)a シンボル またはa リスト
(setf symbol)
それが 名前 の 関数 その中で 環境. 。 2. a シンボル またはa リスト(setf symbol)
.
これを考えると、次のようなフォームはどのように振る舞うべきですか?
((setf car) 10 x)
このセットが必要です x
価値への車の車 10
, 、またはフォームを実行する必要があります (setf car)
(これはエラーになります)、その返品値を関数として使用して、引数で呼び出すようにしてください 10
と x
?一般的なLISPはどちらの動作も指定していませんが、それが悪い選択であることは私にはまったく明確ではありません。結局のところ、私が見る限り、標準では、幅広い範囲のオペレーター名をサポートするために有効なフォームの定義を拡張することを標準に適合させることを妨げるものは何もありません(したがって、SETF-Functionsはここでも本当に役に立ちません)。