Lisp入門書の主要部分を読んだ後でも、特別な演算子(quote)
(または同等の'
)関数が何をするのか理解できませんでしたが、これは私が見たLispコード全体にあります。
それは何をするためのものか?
簡単な回答 デフォルトの評価規則をバイパスし、式 (symbol または s-exp) を評価せずに、入力したとおりに関数に渡します。
長い答え: デフォルトの評価ルール
通常の (後で説明します) 関数が呼び出されると、それに渡されたすべての引数が評価されます。これは、次のように記述できることを意味します。
(* (+ a 2)
3)
これは、と 2(+ a 2)
を評価することにより、 を評価a
します。シンボルの値はa
、現在の変数バインディング セットで検索され、置き換えられます。Saya
は現在、値 3 にバインドされています。
(let ((a 3))
(* (+ a 2)
3))
を取得(+ 3 2)
し、+ が 3 と 2 で呼び出されて 5 が生成されます。元のフォームは現在(* 5 3)
15 を生成しています。
quote
もう説明して!
大丈夫。上記のように、関数へのすべての引数が評価されるため、値ではなくシンボル a
を渡したい場合は、評価したくありません。Lisp シンボルは、その値と、ハッシュ テーブルのキーなど、他の言語で文字列を使用するマーカーの両方を兼ねることができます。
ここでquote
出番です。たとえば、Python アプリケーションからリソース割り当てをプロットしたいが、Lisp でプロットを行うとします。Python アプリで次のようにします。
print("'(")
while allocating:
if random.random() > 0.5:
print(f"(allocate {random.randint(0, 20)})")
else:
print(f"(free {random.randint(0, 20)})")
...
print(")")
次のような出力が得られます (少し見栄えがよくなります):
'((allocate 3)
(allocate 7)
(free 14)
(allocate 19)
...)
quote
デフォルト ルールが適用されない ("tick") 原因について私が言ったことを覚えていますか? 良い。そうしないと、 と の値allocate
がfree
ルックアップされてしまい、それは望ましくありません。私たちのLispでは、次のことをしたい:
(dolist (entry allocation-log)
(case (first entry)
(allocate (plot-allocation (second entry)))
(free (plot-free (second entry)))))
上記のデータの場合、次の一連の関数呼び出しが行われます。
(plot-allocation 3)
(plot-allocation 7)
(plot-free 14)
(plot-allocation 19)
しかし、どうlist
ですか?
ええと、引数を評価したい場合があります。数値と文字列を操作し、結果のリストを返す気の利いた関数があるとします...物事。間違ったスタートを切りましょう:
(defun mess-with (number string)
'(value-of-number (1+ number) something-with-string (length string)))
Lisp> (mess-with 20 "foo")
(VALUE-OF-NUMBER (1+ NUMBER) SOMETHING-WITH-STRING (LENGTH STRING))
おい!それは私たちが望んでいたことではありません。いくつかの引数を選択的に評価し、残りをシンボルとして残したいと考えています。#2を試してください!
(defun mess-with (number string)
(list 'value-of-number (1+ number) 'something-with-string (length string)))
Lisp> (mess-with 20 "foo")
(VALUE-OF-NUMBER 21 SOMETHING-WITH-STRING 3)
だけでなくquote
、しかしbackquote
ずっといい!ちなみに、このパターンは (ほとんどの場合) マクロで非常に一般的であるため、それを行うための特別な構文があります。バッククォート:
(defun mess-with (number string)
`(value-of-number ,(1+ number) something-with-string ,(length string)))
を使用するのと似てquote
いますが、いくつかの引数の前にコンマを付けて明示的に評価するオプションがあります。結果は を使用list
した場合と同じですが、マクロからコードを生成している場合、返されたコードの小さな部分のみを評価したい場合が多いため、バッククォートの方が適しています。短いリストの場合、list
より読みやすくなります。
ねえ、あなたは忘れてしまったquote
!
それで、これは私たちをどこに残しますか?そうそう、quote
実際には何をしますか?引数を評価せずに返すだけです。通常の関数について最初に言ったことを覚えていますか? 一部の演算子/関数は、引数を評価する必要がないことがわかりました。IF など -- 取られなかった場合、else ブランチが評価されるのは望ましくありませんよね? いわゆる特別な演算子は、マクロと一緒に、そのように機能します。特殊演算子は言語の「公理」でもあります -- 最小限の規則のセット -- これに基づいて、Lisp の残りの部分をさまざまな方法で組み合わせることで実装できます。
ただし、に戻りquote
ます。
Lisp> (quote spiffy-symbol)
SPIFFY-SYMBOL
Lisp> 'spiffy-symbol ; ' is just a shorthand ("reader macro"), as shown above
SPIFFY-SYMBOL
(Steel-Bank Common Lisp で) と比較:
Lisp> spiffy-symbol
debugger invoked on a UNBOUND-VARIABLE in thread #<THREAD "initial thread" RUNNING {A69F6A9}>:
The variable SPIFFY-SYMBOL is unbound.
Type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
0: [ABORT] Exit debugger, returning to top level.
(SB-INT:SIMPLE-EVAL-IN-LEXENV SPIFFY-SYMBOL #<NULL-LEXENV>)
0]
spiffy-symbol
現在のスコープには何もないからです!
まとめ
quote
、backquote
(コンマ付き)、およびlist
は、リストを作成するために使用するツールの一部です。リストは値のリストであるだけでなく、ご覧のように軽量 (を定義する必要はありませんstruct
) データ構造として使用できます!
さらに学びたい場合は、Peter Seibel の著書Practical Common Lispをお勧めします。これは、Lisp を学習するための実践的なアプローチであり、すでにプログラミング全般に携わっている場合です。最終的に Lisp の旅の途中で、パッケージも使い始めることになります。Ron Garret のThe Idiot's Guide to Common Lisp Packagesは、それらの良い説明を提供します。
ハッピーハッキング!
「私を評価しないでください」と書かれています。たとえば、リストをコードではなくデータとして使用する場合は、リストの前に引用符を付けます。例えば、
(print '(+ 3 4))
「(+ 3 4)」を
(print (+ 3 4))
出力しますが、「7」を出力します
他の人々がこの質問に見事に答えており、Matthias Benkard は素晴らしい警告を発しています。
後で変更するリストを作成するために引用を使用しないでください。この仕様により、コンパイラは引用符で囲まれたリストを定数として扱うことができます。多くの場合、コンパイラは、メモリ内に単一の値を作成し、定数が表示されるすべての場所からその単一の値を参照することによって、定数を最適化します。つまり、定数を匿名のグローバル変数のように扱うことができます。
これにより、明らかな問題が発生する可能性があります。定数を変更すると、まったく関係のないコードで同じ定数の他の使用法が変更される可能性があります。たとえば、ある関数である変数を '(1 1) と比較し、まったく別の関数で '(1 1) でリストを開始し、さらに要素を追加することができます。これらの関数を実行すると、最初の関数が適切に一致しなくなっていることに気付くかもしれません。これは、変数を '(1 1 2 3 5 8 13) と比較しようとしているからです。これは、2 番目の関数が返した結果です。これら 2 つの関数はまったく無関係ですが、定数を使用しているため、互いに影響を及ぼします。完全に正常なリストの繰り返しが突然無限ループするなど、さらにクレイジーな悪影響が発生する可能性があります。
比較など、定数リストが必要な場合は quote を使用します。結果を変更する場合は list を使用します。
この質問に対する 1 つの回答は、QUOTE は「リスト データ構造を作成する」と述べています。これは正しくありません。QUOTE はこれよりも基本的なものです。実際、QUOTE は単純な演算子です。その目的は、何も起こらないようにすることです。特に、何も作成しません。
(QUOTE X) が言っていることは、基本的に「何もしないで、X をください」ということです。X は (QUOTE (ABC)) のようなリストや (QUOTE FOO) のような記号である必要はありません。どんなオブジェクトでもかまいません。実際、(LIST 'QUOTE SOME-OBJECT) によって生成されたリストを評価した結果は、それが何であれ、常に SOME-OBJECT を返すだけです。
ここで、(QUOTE (ABC)) が A、B、および C を要素とするリストを作成したように見える理由は、そのようなリストが実際に返されるものだからです。しかし、QUOTE フォームが評価される時点で、リストは通常、しばらくの間 (QUOTE フォームのコンポーネントとして!) 存在し、コードの実行前にローダーまたはリーダーによって作成されます。
かなり頻繁に初心者をつまずかせる傾向があるこのことの 1 つの意味は、QUOTE フォームによって返されるリストを変更することは非常に賢明ではないということです。QUOTE によって返されるデータは、すべての意図と目的において、実行されるコードの一部と見なされるため、読み取り専用として扱う必要があります。
引用符は、フォームの実行または評価を防ぎ、代わりにデータに変換します。一般に、データを評価することでデータを実行できます。
quoteはリストデータ構造を作成します。たとえば、以下は同等です。
(quote a)
'a
リスト(またはツリー)の作成にも使用できます。
(quote (1 2 3))
'(1 2 3)
Practical Common Lisp(オンラインで読むことができます)などのlispの入門書を入手するのがおそらく最善です。
引数の値を渡す代わりに、引数自体を渡したい場合は、quote を使用します。これは主に、C プログラミング言語では使用できないリスト、ペア、およびアトムを使用する際のプロシージャ パスに関連しています (ほとんどの人は C プログラミングを使用してプログラミングを開始します。したがって、混乱します) これは Lisp の方言である Scheme プログラミング言語のコードです。このコードを理解できると思います。
(define atom? ; defining a procedure atom?
(lambda (x) ; which as one argument x
(and (not (null? x)) (not(pair? x) )))) ; checks if the argument is atom or not
(atom? '(a b c)) ; since it is a list it is false #f
最後の行 (atom? 'abc) は、abc がアトムかどうかをチェックする手続きに abc をそのまま渡していますが、(atom? abc) を渡すと、abc の値をチェックし、その値を に渡します。それ。それ以来、私たちはそれに価値を提供していません
別の短い答え:
quote
はそれを評価しないことを意味し、backquoteは quote but leave backdoorsです。
良い参考文献:
Emacs Lisp Reference Manual は非常に明確にします
9.3 引用
特別な形式の quote は、評価せずに、書かれているとおりに単一の引数を返します。これにより、自己評価オブジェクトではない定数のシンボルとリストをプログラムに含める方法が提供されます。(数値、文字列、ベクトルなどの自己評価オブジェクトを引用符で囲む必要はありません。)
特殊な形式: quote オブジェクト
This special form returns object, without evaluating it.
クォートはプログラムで頻繁に使用されるため、Lisp には便利な読み取り構文が用意されています。アポストロフィ文字 (''') の後に Lisp オブジェクト (読み取り構文で) が続くと、最初の要素が引用符で、2 番目の要素がオブジェクトであるリストに展開されます。したがって、読み取り構文 'x は (quote x) の省略形です。
引用符を使用する式の例を次に示します。
(quote (+ 1 2))
⇒ (+ 1 2)
(quote foo)
⇒ foo
'foo
⇒ foo
''foo
⇒ (quote foo)
'(quote foo)
⇒ (quote foo)
9.4 バッククォート
逆引用符構造を使用すると、リストを引用できますが、そのリストの要素を選択的に評価できます。最も単純なケースでは、特殊な形式の quote と同じです (前のセクションで説明。Quoting を参照)。たとえば、次の 2 つの形式では同じ結果が得られます。
`(a list of (+ 2 3) elements)
⇒ (a list of (+ 2 3) elements)
'(a list of (+ 2 3) elements)
⇒ (a list of (+ 2 3) elements)
逆引用符の引数内の特別なマーカー「,」は、定数ではない値を示します。Emacs Lisp エバリュエーターは、',' の引数を評価し、値をリスト構造に入れます。
`(a list of ,(+ 2 3) elements)
⇒ (a list of 5 elements)
',' による置換は、リスト構造のより深いレベルでも許可されます。例えば:
`(1 2 (3 ,(+ 4 5)))
⇒ (1 2 (3 9))
特別なマーカー ',@' を使用して、評価された値を結果リストにスプライスすることもできます。結合されたリストの要素は、結果のリストの他の要素と同じレベルの要素になります。'`' を使用しない同等のコードは、多くの場合、判読できません。ここではいくつかの例を示します。
(setq some-list '(2 3))
⇒ (2 3)
(cons 1 (append some-list '(4) some-list))
⇒ (1 2 3 4 2 3)
`(1 ,@some-list 4 ,@some-list)
⇒ (1 2 3 4 2 3)