4

Lisp でのリストの評価について質問があります。

評価される理由(a)(+ a 1)評価されない理由、

(defun test (a) (+ a 1))

ここ(print 4)では評価されない

(if (< 1 2) (print 3) (print 4))

しかし、(print (+ 2 3))ここで評価されます

(test (print (+ 2 3)))

それらが標準ライブラリ関数であることと関係がありますか? Lisp プログラムでそのような関数を定義することは可能ですか?

4

3 に答える 3

6

ご存知かもしれませんが、Lisp 複合フォームは通常、外側から内側に処理されます。フォームを理解するには、最も外側のネストの最初の位置にあるシンボルを見なければなりません。その記号は、形の意味を完全に決定します。次の表現はすべて(b c)、まったく異なる意味を含んでいます。(b c)したがって、最初に部品を分析することによってそれらを理解することはできません。

;; Common Lisp: define a class A derived from B and C
(defclass a (b c) ())

;; Common Lisp: define a function of two arguments
(defun a (b c) ())

;; add A to the result of calling function B on variable C:
(+ a (b c))

伝統的に、Lisp 方言はフォームを演算子フォームと関数呼び出しフォームに分けてきました。演算子形式は完全に恣意的な意味を持ち、その関数をコンパイルまたは解釈するコードによって決定されます (たとえば、評価は関数呼び出しのすべての引数形式を単純に再帰し、結果の値が関数に渡されます)。

初期の歴史から、Lisp はユーザーが独自の演算子を書くことを許可してきました。fexprsこれには、解釈演算子 (歴史的には として知られている) と、マクロとして知られるコンパイル演算子の 2 つのアプローチがありました。どちらも、評価されていない形式を引数として受け取る関数のアイデアに依存しているため、カスタム戦略を実装できるため、新しい動作で評価モデルを拡張できます。

fexpr演算子は、変数の値などを検索できる環境オブジェクトとともに、実行時にフォームを渡すだけです。次に、そのオペレーターがフォームをウォークし、動作を実装します。

マクロ演算子は、マクロ展開時にフォームを渡されます (これは通常、トップレベルのフォームが評価またはコンパイルされる直前に読み込まれるときに発生します)。その仕事は、フォームの動作を解釈することではなく、コードを生成して変換することです。つまり、マクロはミニコンパイラです。(生成されたコードには、さらに多くのマクロ呼び出しを含めることができます。マクロ エキスパンダーがそれを処理し、すべてのマクロ呼び出しがデシメートされるようにします。)

このfexprアプローチは、おそらく非効率的であるため、支持されなくなりました。それは基本的にコンパイルを不可能にしますが、Lisp ハッカーはコンパイルを重視しました。(Lisp は 1960 年頃には既にコンパイル済み言語でした。) このfexprアプローチは字句環境に対しても敵対的です。fexpr関数である が、呼び出されたフォームの変数バインディング環境をピアリングできる必要があります。これは、レキシカルスコープでは許可されていない一種のカプセル化違反です。

マクロの記述は fexprs よりも少し難しく、ある意味では柔軟性に欠けますが、1960 年代から 70 年代にかけて、Lisp ではマクロ記述のサポートが改善され、できる限り簡単に近づけるようになりました。マクロはもともとフォーム全体を受け取り、それを自分で解析する必要がありました。マクロ定義システムは、構文のネストされた側面を含む、分解された構文を簡単に消化できる部分で受け取る引数をマクロ関数に提供するものに発展しました。コード テンプレートを記述するためのバッククォート構文も開発され、コード生成の表現がはるかに簡単になりました。

あなたの質問に答えるために、どうすればそのようなフォームを自分で書くことができますか? たとえば、次の場合:

;; Imitation of old-fashioned technique: receive the whole form,
;; extract parts from it and return the translation.
;; Common Lisp defmacro supports this via the &whole keyword
;; in macro lambda lists which lets us have access to the whole form.
;;
;; (Because we are using defmacro, we need to declare arguments "an co &optional al",
;; to make this a three argument macro with an optional third argument, but
;; we don't use those arguments. In ancient lisps, they would not appear:
;; a macro would be a one-argument function, and would have to check the number
;; of arguments itself, to flag bad syntax like (my-if 42) or (my-if).)
;;
(defmacro my-if (&whole if-form an co &optional al)
  (let ((antecedent (second if-form))   ;; extract pieces ourselves
        (consequent (third if-form))    ;; from whole (my-if ...) form
        (alternative (fourth if-form)))
    (list 'cond (list antecedent consequent) (list t alternative))))

;; "Modern" version. Use the parsed arguments, and also take advantage of
;; backquote syntax to write the COND with a syntax that looks like the code.
(defmacro my-if (antecedent consequent &optional alternative)
   `(cond (,antecedent ,consequent) (t ,alternative))))

もともと Lisp には しかなかったので、これは適切な例ですcondifMcCarthy's Lispにはありませんでした。condその「シンタックス シュガー」は、おそらく上記のように に展開されるマクロとして後で発明されましたmy-if

于 2012-03-23T21:21:45.740 に答える
6

ifそしてdefunマクロです。マクロは、フォームをより長いコードに展開します。展開時には、マクロの引数は評価されません。

関数を書こうとしても、独自の評価戦略を実装する必要があるために苦労している場合は、代わりにマクロを作成する必要があるという強いシグナルです。

免責事項: 使用している Lisp の種類によっては、技術的にはマクロではなく「特殊なフォーム」と呼ばれる場合がありますが、遅延評価の概念は引き続き適用されますifdefun

于 2012-03-23T16:38:55.723 に答える
1

Lisp は、フォームの評価モデルで構成されています。Lisp の方言が異なれば、それらの規則も異なります。

Common Lisp を見てみましょう。

  • データはそれ自体に評価されます
  • 関数形式は、評価された引数で関数を呼び出すことによって評価されます
  • 特殊なフォームは、特殊な演算子ごとに定義された規則に従って評価されます。Common Lisp 標準は、これらすべてをリストし、非公式な方法で何を行うかを定義しており、ユーザーが新しい特殊演算子を定義する方法はありません。
  • マクロ フォームが変換され、結果が評価される

IF、DEFUN などがどのように機能し、何を評価したか、いつ実行し、何を評価しなかったかは、Common Lisp 標準で定義されています。

于 2012-03-23T17:03:53.343 に答える