2011-12-29 2 views
12

私はEmacs Lispでプログラミングを学び始めました。私はシンボル引用によって混乱している。たとえば :Emacs Lispでシンボルを引用するとき

(progn 
    (setq a '(1 2)) 
    (prin1 a) 
    (add-to-list 'a 3) 
    (prin1 a) 
    (setcar a 4) 
    (prin1 a) 
    (push 5 a) 
    "" 
) 

「関数setcar」と「プッシュ」機能は、引数の引用を必要としない一方で、「アドオンのリスト」機能は、最初の引数として引用記号を必要とする理由?

答えて

18

シンボルaとその値を(setq a '(1 2))の後に示す図です。ボックスは基本的なデータ構造(シンボルとコンス)であり、矢印はポインタ(データが別のデータを参照する)です。 (私は少し単純化しています。)

symbol      cons    cons 
+-------+----------+  +------+------+ +------+------+ 
|name: |variable: |  |car: |cdr: | |car: |cdr: | 
| a  | |  |  | 1 | | | | 2 | nil | 
+-------+----|-----+  +------+--|---+ +------+------+ 
      |    ​↑   |  ↑ 
      +-------------+   +-------+ 

表現'(1 2)は、2要素のリストを構成する右側にある2つのコンスを、構築します。式(setq a '(1 2))は、存在しない場合はシンボルaを作成し、その「可変スロット」(シンボルの値を含む部分)を新たに作成されたリストを指し示します。 setqは組み込みマクロであり、(setq a '(1 2))(set 'a '(1 2))の省略形です。 setの最初の引数は変更するシンボルで、2番目の引数はシンボルの可変スロットを設定する値です。

(add-to-list 'a 3)は、ここに(set 'a (cons 3 a))と等価です。これは、3がリストに含まれていないためです。この表現は4つのことを行います:

  1. 新しいコンスセルを作成します。
  2. 新しいコンスセルの車フィールドを3に設定します。
  3. 新しいコンスセルのcdrフィールドをaという以前の(まだ現在の)値に設定します(つまり、aの変数スロットの内容をコピーします)。
  4. 可変スロットaを新しいコンスセルに設定します。

このように、その呼び出し、関連するデータ構造は見た後:

symbol      cons    cons    cons 
+-------+----------+  +------+--|---+ +------+------+ +------+------+ 
|name: |variable: |  |car: |cdr: | |car: |cdr: | |car: |cdr: | 
| a  | |  |  | 3 | | | | 1 | | | | 2 | nil | 
+-------+----|-----+  +------+--|---+ +------+--|---+ +------+------+ 
      |    ​↑   |  ↑   |  ↑ 
      +-------------+   +-------+   +-------+ 

setcarへの呼び出しが任意の新しいデータ構造を作成していない、とシンボルaではなく、その上で動作していませんcar現在(setcar a 4)後3が含まれているコンスセルである値、データ構造は次のようになります。

symbol      cons    cons    cons 
+-------+----------+  +------+--|---+ +------+------+ +------+------+ 
|name: |variable: |  |car: |cdr: | |car: |cdr: | |car: |cdr: | 
| a  | |  |  | 4 | | | | 1 | | | | 2 | nil | 
+-------+----|-----+  +------+--|---+ +------+--|---+ +------+------+ 
      |    ​↑   |  ↑   |  ↑ 
      +-------------+   +-------+   +-------+ 

pushですマクロ。ここでは(push 5 a)(set 'a (cons 5 a))に相当します。

setqpushは(setqは限り我々が懸念しているとして、ここで定義インタプリタに組み込まれており、Lispで提供されていないマクロを意味し、「特別な形式」、である)マクロです。マクロは引数が評価されず、拡張するかどうかを選択できます。 set,setcarおよびadd-to-listは、評価された引数を受け取る関数です。シンボルを評価すると、可変スロットの内容が返されます。最初の(setq a '(1 2))の後にシンボルaの値は1を含むコンスセルです。あなたはまだ混乱している場合

、私はもの((setq b a)を試して、あなたがaにして(シンボルaに作用するものを)行動するときbを修正式のどの自分のために見ないではないことを示唆しています記号aの値に作用する)。

13

関数は実行前に引数を評価するので、実際のシンボルを(たとえばいくつかのデータ構造体へのポインタとして)渡す必要があるときは引用し、変数値のときは引用符で囲みません。

add-to-listは、最初の引数のインプレース突然変異を実行するので、引用符付きのシンボルが必要です。

pushはマクロではありません。そのため、評価なしで引用符で囲まれていない引数を受け入れることができます。 setcarのようなビルトインフォームにもその制限はありません。

+0

マクロまたは関数であるかどうかは、実装を見ていなくても、そのドキュメントから分かりますか? – Tom

+1

@Tomのドキュメントでは、通常、最初の行でpush-'pushは' 'cl.el 'のLispマクロです。 –

+0

はい、でもsetcarはどうですか?ドキュメントから、その議論を評価していないことをどうやって知っていますか? – Tom

8

これまでの一方で、quoteの使用と機能との違いを明確にし、マクロ特殊形式一方与えられた他の回答。

しかし、彼らは質問の別の部分にはいません:なぜadd-to-listのままですか? なぜの最初の引数はシンボルである必要がありますか?それは議論を評価するかどうかという別の質問です。 add-to-listのデザインの背後にあるのは本当の疑問です。

一つは、add-to-listがその引数を評価し、第1引数の値がリストであると予想され、その要素として、そのリストに2つ目の引数の値を加え、その結果を返したことを想像できる新しいリストまたは同じリスト)。それは(add-to-list foo 'shoe)shoeという記号をfooの値(例えば、(1 2 buckle))のリストに追加すると、(1 2 buckle shoe)となります。

このような機能はあまり役に立ちません。です。どうして?リスト値は必ずしもアクセス可能ではないためです。変数fooは、それにアクセスするための方法として、 "ハンドル"または "ポインタ"と考えることができます。しかし、それは関数を返すというリストには当てはまりません。返されるリストは新しいリスト構造で構成され、通常はそのリストを指す変数はありません(変数はありません)。関数add-to-listは、シンボル(変数)fooを見ることはありません。最初の引数として受け取るリスト値がfooにバインドされていることを知る方法がありません。 add-to-listがそのように設計されていれば、返された結果をリスト変数に代入する必要があります。

IOW、add-to-listは関数なのでargsを評価しますが、それはあまり説明しません。その最初のargの値としてシンボルが期待されます。そして、その変数(シンボル)の値がリストであることを期待します。 2番目のargの値をリストに追加します(おそらくリスト構造を変更します)。は、そのリストの最初のargの値である変数の値を設定します。

ボトムライン:そのジョブは(新しい値に追加された新しいリスト要素と同一のリスト値または同じ値であることシンボルに新しい値を割り当てるであるので、それは引数として記号を必要とフロント)。

また、別の方法としては、pushのようにマクロまたは特殊形式を使用することができます。同じ考えです。pushは、シンボルを2番目の引数として使用します。違いは、pushはargsを評価しないので、シンボルを引用符で囲む必要はないという点です。しかし、どちらの場合(Emacs Lisp)でも、コードはシンボルを保持して、の値をに設定する必要があります。

+0

これは一般的に価値のあるポイントだと思いますが、リストの場合は本当に真実ではありません彼らはコンスセルへのポインタです。例えば、(リストの値)(setcdr(最後のリスト)(cons値なし))リストの '(defun my-add-to-list2(list value) – phils

+1

記号の値ではない値をリストに追加する関数を持つことができないということではありませんでした。ポイントは、 'add-to-list'のポイントは変数のリスト値に要素を追加することです。 IOW、 'add-to-list'は全て変数値を設定することです*。それは***ではありません***(その名前にもかかわらず)リストに要素を追加する単なる関数です。 – Drew

+0

重要な違いは、*シンボル*を渡すことによって、他のシンボル*に影響を与えずにそのシンボルの値を変更することができるということです。*評価されたリスト自体を渡すと、リストに影響する可能性があります。 – phils

関連する問題