Lispで '(または引用)を使用するのはいつですか?


114

入門のLispブックの主要部分を読み終えた後も、特別な演算子(quote)(または同等の')関数の機能を理解できませんでした。

それは何をするためのものか?

回答:


178

短い回答 デフォルトの評価ルールをバイパスし、式(シンボルまたはs-exp)を評価せ、入力したとおりに関数に渡します。

長い答え:デフォルトの評価ルール

通常の(後で説明します)関数が呼び出されると、それに渡されるすべての引数が評価されます。これはあなたがこれを書くことができることを意味します:

(* (+ a 2)
   3)

これは(+ a 2)aと2を評価することによってを評価します。シンボルの値はa、現在の変数バインディングセットで検索され、置き換えられます。たとえばa、現在値3にバインドされているとします。

(let ((a 3))
  (* (+ a 2)
     3))

当社は取得したい(+ 3 2)、+ 5.当社独自のフォームが今されて降伏3と2で呼び出される(* 5 3)15を得ました。

quoteもう説明してください!

よし。上記のように、関数へのすべての引数が評価されるため、その値ではなくシンボル を渡しaたい場合は、評価する必要はありません。Lispシンボルは、値と、他の言語ではテーブルをハッシュするためのキーなどの文字列を使用していたマーカーの両方を2倍にすることができます。

これが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)

だけでなくquotebackquote

ずっといい!ちなみに、このパターンは(ほとんどの)マクロで非常に一般的であり、そのための特別な構文があります。逆引用:

(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現在のスコープにはないので!

まとめ

quotebackquote(コンマ付き)、およびlistリストを作成するために使用するツールの一部であり、値のリストであるだけでなく、軽量の(を定義する必要がないstruct)データ構造として使用できます。

詳細を知りたい場合は、すでにプログラミング全般に興味がある場合は、Peter Seibelの著書「Practical Common Lisp」をお勧めします。最終的にはLispの旅で、パッケージの使用も開始します。Ron GarretのCommon Lisp PackagesへのIdiot's Guideは、それらの良い説明を提供します。

ハッキングハッピー!


私のemacsではSBCLがセットアップされており、 `'this' is 'true'と入力すると、出力で最後の、つまりTRUEのみが返されます。ポルタクルでも同じ出力が得られます
トトロ

@Totoro関数またはlisp内の複数のステートメントの戻り値は最後の式であるため、実際にはthis、then is、then trueが返されますが、最後に返されたものしか表示されません。(これは真実であり、別のステートメントです)
ウェズル

52

「私を評価しないでください」と書いてあります。たとえば、リストをコードとしてではなくデータとして使用したい場合は、その前に引用符を付けます。例えば、

(print '(+ 3 4))「(+ 3 4)」を (print (+ 3 4))出力しますが、「7」を出力します


それをどのように評価するunquoteでしょうか、例えば、コマンドはありますか?
ライム

3
@William Lispには、次の便利な関数がありevalます(print (eval '(+ 3 4)))。これがLispを非常に優れたものにしている理由です。リストはコードであり、コードはリストなので、Lispプログラムはそれ自体を操作できます。
darkfeline 2015年

18

他の人々は立派にこの質問に答えました、そして、Matthias Benkardは素晴らしい警告を持ち出します。

後で変更するリストを作成するために引用符を使用しないでください。仕様により、コンパイラは引用符付きリストを定数として扱うことができます。多くの場合、コンパイラーは定数をメモリー内に作成し、定数が出現するすべての場所からその単一の値を参照することによって定数を最適化します。つまり、定数を匿名のグローバル変数のように扱うことができます。

これは明らかな問題を引き起こす可能性があります。定数を変更すると、まったく関係のないコードでの同じ定数の他の使用法が変更される可能性があります。たとえば、いくつかの変数をいくつかの関数で '(1 1)と比較し、完全に異なる関数で'(1 1)でリストを開始し、それにさらに要素を追加できます。これらの関数を実行すると、2番目の関数が返す変数である '(1 1 2 3 5 8 13)と変数を比較しようとしているため、最初の関数が適切に一致しない場合があります。これら2つの関数は完全に無関係ですが、定数を使用しているため、相互に影響します。完全に正常なリストの反復が突然無限ループするような、より奇妙な悪影響さえ発生する可能性があります。

比較など、定数リストが必要な場合は、引用符を使用します。結果を変更する場合は、リストを使用してください。


だから、(list (+ 1 2)) ほとんどの時間を使うべきだと思います。もしそうなら、そのよう(+ 1 2)な例の中の評価をどのように防ぐのですか?unquoteコマンドはありますか?
ライム

1
と同等'((3))か、同等'((+ 1 2))か?後者の場合は、さらに使用する必要がありlistます(list (list '+ 1 2))。または、同等の機能が必要な場合は'(+ 1 2)、ただ(list '+ 1 2)。また、リストを変更しない場合は、引用符を自由に使用し'(+ 1 2)てください。単にそれと比較するだけの場合は問題ありません。
Xanthir、2015年

1
引用されたリストが定数として扱われることになっている場所を参照してもよろしいですか?
ライム

HyperSpec clhs.lisp.se/Body/s_quote.htmは、引用されたオブジェクトが破壊的に変更された場合の動作は未定義であると述べています。これは、implsが値をアトミック値として処理できるようにすることを意味しています。
Xanthir 2015年

14

この質問に対する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によって返されるデータは、すべての意図および目的において、実行されるコードの一部と見なされるため、読み取り専用として扱う必要があります。


11

引用はフォームの実行または評価を防ぎ、代わりにフォームをデータに変換します。一般に、データを評価してから実行できます。

quoteはリストのデータ構造を作成します。たとえば、以下は同等です。

(quote a)
'a

リスト(またはツリー)の作成にも使用できます。

(quote (1 2 3))
'(1 2 3)

おそらく、Practical Common Lisp(オンラインで読むことができます)など、lispに関する入門書を入手することをお勧めします。


3

Emacs Lispでは:

引用できるものは何ですか?

リストとシンボル。

数値を引用すると、数値自体に評価さ '55ます。これはと同じです。

リストを引用するとどうなりますか?

例えば:

'(one two) 評価する

(list 'one 'two) 評価する

(list (intern "one") (intern ("two")))

(intern "one")「one」という名前のシンボルを作成し、「central」ハッシュマップに保存します。そのため、あなたが言うときはいつでも、その'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で定義したクラスを使用すると、このaddELisp-SymbolはPythonで次のように書くことができます。Symbol("add",(lambda x,y: x+y),2)

IRC #emacsの人々にシンボルと引用を説明してくれてありがとう。


2

引数の値を渡すのではなく、引数自体を渡したい場合は、引用符を使用します。これは主に、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の値が確認され、値が渡されます。それ。それ以来、私たちはそれに値を提供していません


1

Quoteは引数の内部表現を返します。引用何をしないのかについての説明があまりにも多く進んだ後、それは電球が続いたときです。REPLが引用したときに関数名を大文字に変換しなかった場合、それは私には気づかなかったかもしれません。

そう。通常のLisp関数は、引数を内部表現に変換し、引数を評価して、関数を適用します。Quoteは引数を内部表現に変換し、それを返します。技術的には「評価しないでください」と言っているのは正しいことですが、それが何をしているかを理解しようとしていたときに、何をしていないのかを説明するとイライラしました。私のトースターもLisp関数を評価しません。しかし、それはトースターの機能を説明する方法ではありません。


1

注釈の短い答え:

quote評価なしの意味であり、バッククォートはクォートですがバックドアを残します

良い参照:

Emacs Lispリファレンスマニュアルはそれを非常に明確にします

9.3引用

特別な形式の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バッククォート

バッククォート構文を使用すると、リストを引用できますが、そのリストの要素を選択的に評価できます。最も単純なケースでは、特別なフォームの引用と同じです(前のセクションで説明。引用を参照)。たとえば、次の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)

1
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)プログラムに意味論的な解釈を与えることにより、スキームで引用され、作成した言語のコードとして解釈されるプログラムで作業します。この場合、データを保持するために引用符を使用する必要があります。そうしないと、外部言語で評価されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.