web-dev-qa-db-ja.com

引用とリストの違いは何ですか?

'(別名quote)を使用してリストを作成します。これを次のように常に使用しています:

> (car '(1 2 3))
1

しかし、期待どおりに機能するとは限りません。たとえば、次のように関数のリストを作成しようとしましたが、機能しませんでした。

> (define math-fns '(+ - * /))
> (map (lambda (fn) (fn 1)) math-fns)
application: not a procedure;
  expected a procedure that can be applied to arguments
  given: '+

listを使用すると、次のように機能します。

> (define math-fns (list + - * /))
> (map (lambda (fn) (fn 1)) math-fns)
'(1 -1 1 1)

どうして? 'は便利な省略表現でしたが、なぜ動作が異なるのですか?

39
Alexis King

TL; DR:それらは異なります。疑わしい場合はlistを使用してください。

経験則:引数を評価する場合は常にlistを使用します。 quoteは引数に「分散」するため、'(+ 1 2)は_(list '+ '1 '2)_のようになります。関数ではなく、シンボルがリストに表示されます。


listquoteの詳細

SchemeとRacketでは、quotelistまったく異なるものですが、どちらもリストの作成に使用できるため、混乱が一般的で理解可能です。それらの間には非常に重要な違いがあります:listは単純な古いfunctionですが、quote(特別な_'_構文なしでも)は- 特別な形式。つまり、listはプレーンスキームで実装できますが、quoteは実装できません。

list関数

list関数は実際には2つのうちはるかに単純なので、それから始めましょう。これは、任意の数の引数を取る関数であり、引数をリストに収集します。

_> (list 1 2 3)
(1 2 3)
_

上記の例は、結果がquoteable s式として出力され、trueである場合、混乱を招く可能性があります。この場合、2つの構文は同等です。しかし、もう少し複雑になると、それが異なることがわかります。

_> (list 1 (+ 1 1) (+ 1 1 1))
(1 2 3)
> '(1 (+ 1 1) (+ 1 1 1))
(1 (+ 1 1) (+ 1 1 1))
_

quoteの例では何が起こっていますか?それについてはすぐに説明しますが、最初にlistを見てください。これは単なる通常の関数であるため、標準のScheme評価セマンティクスに従います。引数のそれぞれを評価しますbefore関数に渡されます。つまり、_(+ 1 1)_のような式は、リストに収集される前に_2_に削減されます。

この動作は、変数をリスト関数に提供するときにも表示されます。

_> (define x 42)
> (list x)
(42)
> '(x)
(x)
_

listでは、xlistに渡される前に評価されます。 quoteを使用すると、状況はさらに複雑になります。

最後に、listは単なる関数なので、高階の方法を含め、他の関数と同じように使用できます。たとえば、map関数に渡すことができ、適切に機能します。

_> (map list '(1 2 3) '(4 5 6))
((1 4) (2 5) (3 6))
_

quoteフォーム

listとは異なり、引用はLispの特別な部分です。 quoteフォームは、特別なリーダー略語__'_を取得するという点で特別ですが、それがなくてもalso特別です。 listとは異なり、quotenot関数であるため、関数のように動作する必要はありません。独自のルールがあります。

LISPソースコードの簡単な説明

SchemeとRacketが派生しているLISPでは、すべてのコードは実際には通常のデータ構造で構成されています。たとえば、次の式について考えてみます。

_(+ 1 2)
_

その式は実際にはa listであり、3つの要素があります。

  • _+_シンボル
  • 数_1_
  • 数_2_

これらの値はすべて、プログラマーが作成できる通常の値です。 _1_の値は、それ自体が評価されるため、簡単に作成できます。つまり、_1_と入力するだけです。しかし、シンボルとリストはより困難です。デフォルトでは、ソースコード内のシンボルは変数ルックアップを実行します。つまり、シンボルは自己評価ではありません。

_> 1
1
> a
a: undefined
  cannot reference undefined identifier
_

しかし、結局のところ、シンボルは基本的に単なる文字列であり、実際にはそれらの間で変換できます。

_> (string->symbol "a")
a
_

デフォルトでは、ソースコードのリスト関数を呼び出す! _(+ 1 2)_を実行すると、リストの最初の要素である_+_シンボルが参照されるため、リストはシンボル以上の機能を備えています。 、それに関連付けられている関数を検索し、リスト内の残りの要素とともに呼び出します。

ただし、この「特別な」動作を無効にしたい場合もあります。リストを取得するか、評価せずにシンボルを取得することができます。これを行うには、quoteを使用できます。

引用の意味

これらすべてを念頭に置いて、quoteが何をするかは非常に明白です。ラップする式の特別な評価動作を「オフにする」だけです。たとえば、シンボルをquoteingすることを検討してください:

_> (quote a)
a
_

同様に、リストをquoteingすることを検討してください:

_> (quote (a b c))
(a b c)
_

quoteを何に指定しても、常にalways吐き出されます。それ以上でもそれ以下でもありません。つまり、リストを指定した場合、部分式は評価されません。それらが評価されることを期待しないでください!何らかの評価が必要な場合は、listを使用してください。

さて、あなたは尋ねるかもしれません:あなたがシンボルやリスト以外のものをquoteした場合どうなりますか?ええと、答えは...何もありません!あなたはそれを取り戻すだけです。

_> (quote 1)
1
> (quote "abcd")
"abcd"
_

quoteは、与えたものを正確に吐き出すだけなので、これは理にかなっています。これが、数値や文字列のような「リテラル」がLISP用語で「自己引用」と呼ばれることがある理由です。

もう1つ:quoteを含む式をquoteするとどうなりますか?つまり、「double quote」を実行するとどうなるでしょうか。

_> (quote (quote 3))
'3
_

そこで何が起こった?まあ、_'_は実際にはquoteの直接の省略形であることを忘れないでください。そのため、特別なことは何も起こりませんでした。実際、Schemeに印刷時に略語を無効にする方法がある場合、次のようになります。

_> (quote (quote 3))
(quote 3)
_

quoteが特別であることにだまされないでください。_(quote (+ 1))_と同様に、ここでの結果は単純な古いリストです。実際、リストから最初の要素を取得できます。それがどうなるかを推測できますか?

_> (car (quote (quote 3)))
quote
_

_3_を推測した場合、それは間違いです。 quoteすべての評価を無効にすることに注意してください。quote記号を含む式は、単なるプレーンリストです。 REPLでこれに慣れるまでこれで遊んでください。

_> (quote (quote (quote 3)))
''3
(quote (1 2 (quote 3)))
(1 2 '3)
_

見積もりは信じられないほど単純ですが、従来の評価モデルに対する私たちの理解に反する傾向があるため、非常に複雑になる可能性があります。実際、それは混乱しています理由それがどれほど単純であるかということです:特別なケースはなく、ルールもありません。これは、指定したとおりに正確に返すだけです(したがって、「引用」という名前です)。


付録A:準見積

それで、見積りが評価を完全に無効にする場合、それは何に役立ちますか?まあ、文字列、記号、または数値のリストを作成することは別として、すべてが事前にわかっていることはあまりありません。幸い、quasiquotationの概念は、引用符から抜け出し、通常の評価に戻る方法を提供します。

基本は非常に単純です。quoteを使用する代わりに、quasiquoteを使用します。通常、これはquoteとまったく同じように機能します。

_> (quasiquote 3)
3
> (quasiquote x)
x
> (quasiquote ((a b) (c d)))
((a b) (c d))
_

quasiquoteを特別にするのは、特別な記号unquoteを認識することです。 unquoteがリストのどこにある場合でも、そこに含まれる任意の式に置き換えられます。

_> (quasiquote (1 2 (+ 1 2)))
(1 2 (+ 1 2))
> (quasiquote (1 2 (unquote (+ 1 2))))
(1 2 3)
_

これにより、quasiquoteを使用して、unquoteで埋められる「穴」を持つソートのテンプレートを作成できます。つまり、引用符で囲まれたリスト内に変数の値を実際に含めることが可能です。

_> (define x 42)
> (quasiquote (x is: (unquote x)))
(x is: 42)
_

もちろん、quasiquoteunquoteの使用はかなり冗長であるため、_'_のように、それぞれに独自の省略形があります。具体的には、quasiquoteは_`_(バックティック)であり、unquoteは_,_(カンマ)です。これらの略語を使用すると、上記の例ははるかに口当たりが良くなります。

_> `(x is: ,x)
(x is: 42)
_

最後のポイントの1つは、quasiquoteが実際にcanかなり毛深いマクロを使用してラケットに実装されているということです。 listcons、そしてもちろんquoteの使用法に拡張されます。


付録B:Schemeでのlistおよびquoteの実装

listの実装は、「残余引数」構文がどのように機能するかにより、非常に簡単です。これで十分です。

_(define (list . args)
  args)
_

それでおしまい!

対照的に、quoteははるかに困難です。実際、不可能です。評価を無効にするという考えはマクロとよく似ているので、それは完全に実現可能に思えます。しかし、素朴な試みは問題を明らかにします:

_(define fake-quote
  (syntax-rules ()
    ((_ arg) arg)))
_

argを取り、それを吐き出すだけですが、これは機能しません。何故なの?さて、私たちのマクロの結果が評価されるので、すべてが無意味です。次のように、_(list ...)_に展開し、要素を再帰的に引用することで、quoteのようなものに展開できる場合があります。

_(define impostor-quote
  (syntax-rules ()
    ((_ (a . b)) (cons (impostor-quote a) (impostor-quote b)))
    ((_ (e ...)) (list (impostor-quote e) ...))
    ((_ x)       x)))
_

残念ながら、手続き型マクロがないと、quoteがないとシンボルを処理できません。 _syntax-case_を使用して近づけることはできますが、それでもquoteの動作をエミュレートするだけで、複製はしません。


付録C:ラケットの印刷規則

この回答の例をラケットで試すと、期待どおりに印刷されない場合があります。多くの場合、次の例のように、先頭に_'_が付いて印刷されることがあります。

_> (list 1 2 3)
'(1 2 3)
_

これは、ラケットがデフォルトで、可能な場合は結果を式として出力するためです。つまり、結果をREPLに入力して同じ値を返すことができるはずです。私は個人的にこの動作を見つけましたニースですが、引用を理解しようとすると混乱する可能性があります。オフにする場合は、_(print-as-expression #f)_を呼び出すか、DrRacket言語メニューで印刷スタイルを「書き込み」に変更します。

81
Alexis King