質問
Lisp 入門本の主要部分を読み終えた後でも、特殊な演算子が何であるのか理解できませんでした。 (quote)
(または同等の '
) 関数は機能しますが、これは私が見た Lisp コード全体に当てはまります。
それは何をするためのものか?
解決
短い答えデフォルトの評価ルールをバイパスして、 ない 式 (シンボルまたは 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
すでに!
大丈夫。上で見たように、関数へのすべての引数が評価されるため、 シンボル 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
(「チェックマーク」) によってデフォルトのルールが適用されなくなりますか?良い。そうしないと、次のような値が発生します。 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
) データ構造!
さらに詳しく知りたい場合は、ピーター・セイベルの本をお勧めします 実用的な Common Lisp すでにプログラミング全般に興味がある場合は、Lisp を学習するための実践的なアプローチについて説明します。Lisp を使っていくうちに、最終的にはパッケージも使い始めることになります。ロン・ギャレットの Common Lisp パッケージに関する馬鹿のためのガイド それらについて良い説明をします。
ハッピーハッキング!
他のヒント
「私を評価しないでください」ということです。たとえば、リストをコードとしてではなくデータとして使用したい場合は、リストの前に引用符を置きます。例えば、
(print '(+ 3 4))
「(+ 3 4)」を出力しますが、(print (+ 3 4))
「7」を印刷します
この質問には他の人も見事に答えており、マティアス・ベンカードは素晴らしい警告を発しています。
後で変更するリストを作成するために引用符を使用しないでください。この仕様により、コンパイラは引用符で囲まれたリストを定数として扱うことができます。多くの場合、コンパイラは定数の単一の値をメモリ内に作成し、定数が出現するすべての場所からその単一の値を参照することによって定数を最適化します。言い換えれば、定数を匿名のグローバル変数のように扱うことができます。
これにより、明らかな問題が発生する可能性があります。定数を変更すると、まったく関係のないコード内での同じ定数の他の使用法も変更される可能性があります。たとえば、ある変数をある関数で '(1 1) と比較し、まったく別の関数で '(1 1) でリストを開始し、そこにさらに要素を追加する場合があります。これらの関数を実行すると、最初の関数は、変数を '(1 1 2 3 5 8 13) (2 番目の関数が返したもの) と比較しようとしているため、もう適切に一致していないことがわかります。これら 2 つの関数はまったく関連しませんが、定数を使用するため相互に影響を及ぼします。完全に正常なリストの反復が突然無限ループになるなど、さらに恐ろしい悪影響が発生する可能性があります。
比較など、定数リストが必要な場合は引用符を使用します。結果を変更する場合は list を使用します。
この質問に対する 1 つの答えは、QUOTE が「リスト データ構造を作成する」ということです。これは正しくありません。QUOTE はこれよりも基本的なものです。実際、QUOTE は簡単な演算子です。その目的は、 防ぐ 何も起こらないように。特に何も生み出さない。
(引用X)と言っているのは、基本的に「何もしないで、Xを与えてください」 xは(QUOTE(ABC))のようなリストであるか、(QUOTE foo)のようなシンボルである必要はありません。それはどんなオブジェクトでも構いません。実際、(LIST 'QUOTE SOME-OBJECT) によって生成されるリストの評価結果は、それが何であれ、常に SOME-OBJECT を返します。
さて、(QUOTE (A B C)) が A、B、C を要素とするリストを作成したように見える理由は、そのようなリストが実際に返されるものだからです。しかし、QUOTE フォームが評価される時点では、通常、リストはしばらくの間 (QUOTE フォームのコンポーネントとして!) すでに存在しており、コードの実行前にローダーまたはリーダーによって作成されます。
これが意味するところは、初心者がかなり頻繁につまずく傾向があり、QUOTE フォームから返されるリストを変更するのは非常に賢明ではないということです。QUOTE によって返されたデータは、あらゆる意図と目的において、 コード 実行されるため、読み取り専用として扱う必要があります。
引用符はフォームの実行や評価を妨げ、代わりにフォームをデータに変換します。一般に、データを評価することでデータを実行できます。
quote はリスト データ構造を作成します。たとえば、以下は同等です。
(quote a)
'a
リスト (またはツリー) の作成にも使用できます。
(quote (1 2 3))
'(1 2 3)
おそらく、次のような Lisp の入門書を入手するのが最善です。 実用的な Common Lisp (オンラインで読むことができます)。
引数の値を渡す代わりに引数自体を渡したい場合は、引用符を使用します。これは主に、Cプログラミング言語では利用できないリスト、ペア、原子の使用中に合格する手順に関連しています(ほとんどの人はCプログラミングを使用してプログラミングを開始するため、混乱します)これは、LISPの方言であるスキームプログラミング言語のコードですそして、あなたはこのコードを理解できると思います。
(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
最後の行(アトム?'abc) は、abc がアトムかどうかをチェックする手続きに abc をそのまま渡していますが、渡すと (atom?abc) 次に、abc の値をチェックし、値をそれに渡します。それ以来、私たちはそれに何の価値も提供していない
Emacs Lisp の場合:
何が引用できるのでしょうか?
リストとシンボル。
数値を引用すると、その数値自体が評価されます。'5
と同じです 5
.
リストを引用するとどうなりますか?
例えば:
'(one two)
と評価される
(list 'one 'two)
と評価されるもの
(list (intern "one") (intern ("two")))
.
(intern "one")
「one」という名前のシンボルを作成し、それを「中央」ハッシュマップに保存します。 'one
次に、という名前のシンボル "one"
中央のハッシュマップで検索されます。
しかし、シンボルとは何でしょうか?
たとえば、OO 言語 (Java/Javascript/Python) では、シンボルは次のようなオブジェクトとして表現できます。 name
フィールド。これはシンボルの名前です。 "one"
このオブジェクトにはデータやコードを関連付けることができます。
したがって、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
したがって、たとえば、 Symbol
上記で Python で定義したクラス、これ add
ELisp-Symbol は Python で次のように記述できます。 Symbol("add",(lambda x,y: x+y),2)
.
シンボルと引用符について説明してくれた IRC #emacs の方々に感謝します。
Quoteは引数の内部表現を返します。どの引用についてあまりにも多くの説明を調べた後、 しません そうしますと、そのとき電球が点灯しました。私が関数名を引用したときに REPL が関数名を大文字に変換しなかったら、気付かなかったかもしれません。
それで。通常の Lisp 関数は、引数を内部表現に変換し、引数を評価し、関数を適用します。Quoteは引数を内部表現に変換し、それを返すだけです。この引用文に「評価しない」と書かれているのは技術的には正しいのですが、それが何をするのかを理解しようとしていたとき、それが何をしないのかを説明するのはイライラしました。私のトースターは Lisp 関数も評価しません。しかし、それではトースターの機能を説明できません。
もう一つの短い答え:
quote
評価せずに意味し、 逆引用符 引用ですが放置します バックドア.
良い参考資料:
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)
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)
) プログラムにセマンティックな解釈を与えることで、作成した言語のコードとして解釈されます。この場合、データを保持するには引用符を使用する必要があります。そうしないと、データは外部言語で評価されます。