LISP入門書の主要部分を読み終えた後、特別な演算子(quote)
(または同等の'
)関数は機能しますが、これは私が見たすべてのLISPコード上にあります。
それは何をするためのものか?
短い答えデフォルトの評価ルールをバイパスし、式を評価しますnot(シンボルまたはs-exp) 、入力したとおりに関数に渡します。
ロングアンサー:デフォルトの評価ルール
通常の(後で説明します)関数が呼び出されると、渡されたすべての引数が評価されます。これは、これを書くことができることを意味します。
(* (+ a 2)
3)
次に、a
および2を評価することにより、(+ a 2)
を評価します。シンボルa
の値は、現在の変数バインディングセットで検索され、置換されます。 a
は現在値3にバインドされているとします:
(let ((a 3))
(* (+ a 2)
3))
(+ 3 2)
を取得すると、3と2で+が呼び出され、5が生成されます。元のフォームは(* 5 3)
が15を生成します。
説明quote
すでに!
わかった。上記のように、関数へのすべての引数が評価されるため、値ではなくsymbola
を渡したい場合は、評価してください。 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
(「ティック」)について私が言ったことを覚えていますか?良い。そうでなければ、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
はどうですか?
まあ、時々あなたはdo引数を評価したいです。数字と文字列を操作し、結果のリストを返す気の利いた関数があるとしましょう。間違ったスタートをしましょう:
(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
は実際に何をしますか?評価されていない引数を返すだけです!通常の機能について最初に言ったことを覚えていますか?一部の演算子/関数は、引数をnot評価する必要があることがわかりました。 IFなど-elseブランチが使用されなかった場合、評価されたくないでしょうか?いわゆる特殊演算子は、マクロと一緒に、そのように機能します。特別な演算子は、言語の「公理」であり、最小限のルールセットです。これに基づいて、さまざまな方法でLISPを組み合わせて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の 『Lidiパッケージの一般的なばかげたガイド』 は、それらについての良い説明を提供します。
ハッピーハッキング!
「評価しないでください」と表示されます。たとえば、リストをコードとしてではなくデータとして使用する場合は、その前に引用符を付けます。例えば、
(print '(+ 3 4))
は「(+ 3 4)」を出力しますが、(print (+ 3 4))
「7」を印刷
他の人々はこの質問に見事に答えており、マティアス・ベンカードは素晴らしい警告を出します。
QUOTEを使用して、後で変更するリストを作成しないでください。この仕様により、コンパイラは引用符付きリストを定数として扱うことができます。多くの場合、コンパイラは、メモリ内に定数の単一の値を作成し、定数が現れるすべての場所からその単一の値を参照することにより、定数を最適化します。つまり、定数を匿名のグローバル変数のように扱う場合があります。
これは明らかな問題を引き起こす可能性があります。定数を変更すると、まったく関係のないコードで同じ定数の他の用途が非常によく変更される可能性があります。たとえば、ある関数で変数を '(1 1)と比較し、完全に異なる関数では、リストを'(1 1)で開始してから、さらに多くのものを追加できます。これらの関数を実行すると、変数を2番目の関数が返した '(1 1 2 3 5 8 13)と比較しようとしているため、最初の関数はもはや適切に一致しないことがわかります。これら2つの関数は完全に無関係ですが、定数を使用しているため、互いに影響を及ぼします。完全に正常なリストの反復が突然無限ループするように、さらにクレイジーな悪影響が発生する可能性があります。
比較などのために定数リストが必要な場合は、引用符を使用します。結果を変更するときにリストを使用します。
この質問に対する1つの答えは、QUOTEが「リストデータ構造を作成する」ことです。これは正しくありません。 QUOTEはこれよりも基本的です。実際、QUOTEは取るに足らない演算子です。その目的は、防止何も起こらないようにすることです。特に、何も作成しません。
(QUOTE X)は基本的に「何もしないで、Xをくれ」と言っています。Xは(QUOTE(A B C))のようなリストや(QUOTE FOO)のようなシンボルである必要はありません。どんなオブジェクトでもかまいません。実際、(LIST 'QUOTE SOME-OBJECT)によって生成されたリストを評価した結果は、それが何であれ常に常にSOME-OBJECTを返します。
さて、(QUOTE(A B C))は、要素がA、B、Cであるリストを作成したかのように見えるのは、そのようなリストが実際に返されるものだからです。しかし、QUOTEフォームが評価される時点では、リストは一般に(QUOTEフォームのコンポーネントとして)しばらく存在し、コードの実行前にローダーまたはリーダーによって作成されています。
かなり頻繁に初心者をつまずかせる傾向があることの1つの意味は、QUOTEフォームによって返されるリストを変更することは非常に賢明ではないということです。 QUOTEから返されるデータは、すべての意図と目的のために、実行中のcodeの一部と見なされるため、読み取り専用として扱う必要があります!
引用符は、フォームの実行または評価を防ぎ、代わりにデータに変換します。一般的には、データを評価して実行できます。
quoteはリストデータ構造を作成します。たとえば、次は同等です。
(quote a)
'a
リスト(またはツリー)の作成にも使用できます。
(quote (1 2 3))
'(1 2 3)
Practical Common LISP (オンラインで読むことができます)のような、LISPの入門書を手に入れた方がいいでしょう。
引数の値を渡すのではなく、引数自体を渡したい場合は、引用符を使用します。これは、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がatomであるかどうかを確認する手順にそのままabcを渡しますが、(atom?abc)を渡すと、 abcの値を渡し、値を渡します。なぜなら、値を提供していないからです。
Emacs LISPの場合:
引用できるもの
リストとシンボル。
数値を引用すると、数値自体に評価されます:_'5
_は_5
_と同じです。
リストを引用するとどうなりますか?
例えば:
'(one two)
は以下に評価されます
_(list 'one 'two)
_は評価されます
_(list (intern "one") (intern ("two")))
_。
_(intern "one")
_は「one」という名前のシンボルを作成し、それを「central」ハッシュマップに保存します。したがって、_'one
_と言うときはいつでも_"one"
_という名前のシンボルがその中央のハッシュで検索されます-地図。
ただし、シンボルとは?
たとえば、OO言語(Java/Javascript/Python)では、シンボルは、上記の_"one"
_のようなシンボルの名前であるname
フィールドと、データおよび/または、このオブジェクトにコードを関連付けることができます。
したがって、Pythonのシンボルは次のように実装できます。
_class Symbol:
def __init__(self,name,code,value):
self.name=name
self.code=code
self.value=value
_
たとえば、Emacs LISPでは、シンボルに1)データを関連付けることができ、さらに(同じシンボルに対して)2)コードを関連付けることができます-コンテキストに応じて、データまたはコードが呼び出されます。
たとえば、Elispの場合:
_(progn
(fset 'add '+ )
(set 'add 2)
(add add add)
)
_
_4
_と評価されます。
_(add add add)
_は次のように評価されるためです。
_(add add add)
(+ add add)
(+ 2 add)
(+ 2 2)
4
_
したがって、たとえば、上記のPythonで定義したSymbol
クラスを使用すると、このadd
ELisp-SymbolはPythonでSymbol("add",(lambda x,y: x+y),2)
。
IRC #emacsの皆さんに、シンボルと引用を説明してくれてありがとう。
_Code is data and data is code. There is no clear distinction between them.
_
これは、LISPプログラマーが知っている古典的な声明です。
コードを引用すると、そのコードはデータになります。
_1 ]=> '(+ 2 3 4)
;Value: (+ 2 3 4)
1 ]=> (+ 2 3 4)
;Value: 9
_
コードを引用すると、結果はそのコードを表すデータになります。そのため、プログラムを表すデータを使用する場合は、そのプログラムを引用します。これは、リストだけでなく、アトミック式にも有効です。
_1 ]=> 'code
;Value: code
1 ]=> '10
;Value: 10
1 ]=> '"ok"
;Value: "ok"
1 ]=> code
;Unbound variable: code
_
LISPに組み込まれたプログラミング言語を作成するとします-スキームで引用されたプログラム('(+ 2 3)
など)で作業し、プログラムにセマンティックを与えることで、作成した言語のコードとして解釈されます解釈。この場合、引用符を使用してデータを保持する必要があります。そうしないと、外部言語で評価されます。
Quoteは、引数の内部表現を返します。 does n'tが何を引用するかについての説明をやりすぎた後、電球が点灯しました。 REPL=を引用したときに関数名を大文字に変換しなかった場合、私には気づかなかったかもしれません。
そう。通常のLISP関数は、引数を内部表現に変換し、引数を評価して、関数を適用します。 Quoteは引数を内部表現に変換し、それを返します。技術的には、引用は「評価しない」と言うのは正しいのですが、それが何をしたのかを理解しようとしていたときに、何をしないのかと言うとイライラしていました。私のトースターもLISP機能を評価しません。しかし、それはトースターの機能を説明する方法ではありません。
短い答え:
quote
は評価せず、backquoteは引用ですが、back doors。
良い参照:
Emacs LISPリファレンスマニュアルでわかりやすく説明
9.3引用
特殊形式のクォートは、評価せずに、記述されているとおりに単一の引数を返します。これにより、自己評価オブジェクトではない定数シンボルとリストをプログラムに含めることができます。 (数値、文字列、ベクトルなどの自己評価オブジェクトを引用する必要はありません。)
特別な形式:引用オブジェクト
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バッククォート
バッククォート構造を使用すると、リストを引用できますが、そのリストの要素を選択的に評価できます。最も単純なケースでは、特殊形式の引用と同じです(前のセクションで説明しています。引用を参照してください)。たとえば、次の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)